Trunk: Merged the Devel-branch (branches/griatch) into /trunk. This constitutes a major refactoring of Evennia. Development will now continue in trunk. See the wiki and the past posts to the mailing list for info. /Griatch

This commit is contained in:
Griatch 2010-08-29 18:46:58 +00:00
parent df29defbcd
commit f83c2bddf8
222 changed files with 22304 additions and 14371 deletions

View file

@ -1,543 +0,0 @@
"""
Batch processor
The batch processor accepts 'batchcommand files' e.g 'batch.ev', containing a
sequence of valid evennia commands in a simple format. The engine
runs each command in sequence, as if they had been run at the terminal prompt.
This way entire game worlds can be created and planned offline; it is
especially useful in order to create long room descriptions where a
real offline text editor is often much better than any online text editor
or prompt.
Example of batch.ev file:
----------------------------
# batch file
# all lines starting with # are comments; they also indicate
# that a command definition is over.
@create box
# this comment ends the @create command.
@set box=desc: A large box.
Inside are some scattered piles of clothing.
It seems the bottom of the box is a bit loose.
# Again, this comment indicates the @set command is over. Note how
# the description could be freely added. Excess whitespace on a line
# is ignored. An empty line in the command definition is parsed as a \n
# (so two empty lines becomes a new paragraph).
@teleport #221
# (Assuming #221 is a warehouse or something.)
# (remember, this comment ends the @teleport command! Don'f forget it)
@drop box
# Done, the box is in the warehouse! (this last comment is not necessary to
# close the @drop command since it's the end of the file)
-------------------------
An example batch file is found in game/gamesrc/commands/examples.
"""
import os
import re
from django.conf import settings
from src import logger
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE
from src.statetable import GLOBAL_STATE_TABLE
#global defines for storage
STATENAME="_interactive batch processor"
cwhite = r"%cn%ch%cw"
cred = r"%cn%ch%cr"
cgreen = r"%cn%ci%cg"
cyellow = r"%cn%ch%cy"
cnorm = r"%cn"
def read_batchbuild_file(filename):
"""
This reads the contents of batchfile.
Filename is considered to be the name of the batch file
relative the directory specified in settings.py
"""
filename = os.path.abspath("%s/%s" % (settings.BATCH_IMPORT_PATH, filename))
try:
f = open(filename)
except IOError:
logger.log_errmsg("file %s not found." % filename)
return None
lines = f.readlines()
f.close()
return lines
def parse_batchbuild_file(filename):
"""
This parses the lines of a batchfile according to the following
rules:
1) # at the beginning of a line marks the end of the command before it.
It is also a comment and any number of # can exist on subsequent
lines (but not inside comments).
2) Commands are placed alone at the beginning of a line and their
arguments are considered to be everything following (on any
number of lines) until the next comment line beginning with #.
3) Newlines are ignored in command definitions
4) A completely empty line in a command line definition is condered
a newline (so two empty lines is a paragraph).
5) Excess spaces and indents inside arguments are stripped.
"""
#read the indata, if possible.
lines = read_batchbuild_file(filename)
if not lines:
logger.log_errmsg("File %s not found." % filename)
return
#helper function
def identify_line(line):
"""
Identifies the line type (comment, commanddef or empty)
"""
try:
if line.strip()[0] == '#':
return "comment"
else:
return "commanddef"
except IndexError:
return "empty"
commands = []
curr_cmd = ""
#purge all superfluous whitespace and newlines from lines
reg1 = re.compile(r"\s+")
lines = [reg1.sub(" ",l) for l in lines]
#parse all command definitions into a list.
for line in lines:
typ = identify_line(line)
if typ == "commanddef":
curr_cmd += line
elif typ == "empty" and curr_cmd:
curr_cmd += "\r\n"
else: #comment
if curr_cmd:
commands.append(curr_cmd.strip())
curr_cmd = ""
if curr_cmd: commands.append(curr_cmd.strip())
#second round to clean up now merged line edges etc.
reg2 = re.compile(r"[ \t\f\v]+")
commands = [reg2.sub(" ",c) for c in commands]
#remove eventual newline at the end of commands
commands = [c.strip('\r\n') for c in commands]
return commands
def batch_process(source_object, commands):
"""
Process a file straight off.
"""
for i, command in enumerate(commands):
cmdname = command[:command.find(" ")]
source_object.emit_to("%s== %s%02i/%02i: %s %s%s" % (cgreen,cwhite,i+1,
len(commands),
cmdname,
cgreen,"="*(50-len(cmdname))))
source_object.execute_cmd(command)
#main access function @batchprocess
def cmd_batchprocess(command):
"""
@batchprocess - build from batch file
Usage:
@batchprocess[/interactive] <filename with full path>
Runs batches of commands from a batchfile. This is a
superuser command, intended for large-scale offline world
development.
Interactive mode allows the user more control over the
processing of the file.
"""
source_object = command.source_object
#check permissions; this is a superuser only command.
if not source_object.is_superuser():
source_object.emit_to(defines_global.NOPERMS_MSG)
return
args = command.command_argument
if not args:
source_object.emit_to("Usage: @batchprocess[/interactive] <path/to/file>")
return
filename = args.strip()
#parse indata file
commands = parse_batchbuild_file(filename)
if not commands:
string = "'%s' not found.\nYou have to supply the real path "
string += "of the file relative to \nyour batch-file directory (%s)."
source_object.emit_to(string % (filename, settings.BATCH_IMPORT_PATH))
return
switches = command.command_switches
if switches and switches[0] in ['inter','interactive']:
# Allow more control over how batch file is executed
if source_object.has_flag("ADMIN_NOSTATE"):
source_object.unset_flag("ADMIN_NOSTATE")
string = cred + "\nOBS: Flag ADMIN_NOSTATE unset in order to "
string += "run Interactive mode. Don't forget to re-set "
string += "it (if you need it) after you're done."
source_object.emit_to(string)
# Set interactive state directly
source_object.cache.state = STATENAME
# Store work data in cache
source_object.cache.batch_cmdstack = commands
source_object.cache.batch_stackptr = 0
source_object.cache.batch_filename = filename
source_object.emit_to("\nBatch processor - Interactive mode for %s ..." % filename)
show_curr(source_object)
else:
set_admin_nostate = False
if not source_object.has_flag("ADMIN_NOSTATE"):
source_object.set_flag("ADMIN_NOSTATE")
set_admin_nostate = True
source_object.emit_to("Running Batch processor - Automatic mode for %s ..." % filename)
source_object.clear_state()
batch_process(source_object, commands)
source_object.emit_to("%s== Batchfile '%s' applied." % (cgreen,filename))
if set_admin_nostate:
source_object.unset_flag("ADMIN_NOSTATE")
GLOBAL_CMD_TABLE.add_command("@batchprocess", cmd_batchprocess,
priv_tuple=("genperms.process_control",), help_category="Building")
# The Interactive batch processor state
def show_curr(source_object,showall=False):
"Show the current command."
ptr = source_object.cache.batch_stackptr
commands = source_object.cache.batch_cmdstack
if ptr >= len(commands):
s = "\n You have reached the end of the batch file."
s += "\n Use qq to exit or bb to go back."
source_object.emit_to(s)
source_object.cache.batch_stackptr = len(commands)-1
show_curr(source_object)
return
command = commands[ptr]
cmdname = command[:command.find(" ")]
s = "%s== %s%02i/%02i: %s %s===== %s %s%s" % (cgreen,cwhite,
ptr+1,len(commands),
cmdname,cgreen,
"(hh for help)",
"="*(35-len(cmdname)),
cnorm)
if showall:
s += "\n%s" % command
source_object.emit_to(s)
def process_commands(source_object, steps=0):
"process one or more commands "
ptr = source_object.cache.batch_stackptr
commands = source_object.cache.batch_cmdstack
if steps:
try:
cmds = commands[ptr:ptr+steps]
except IndexError:
cmds = commands[ptr:]
for cmd in cmds:
#this so it is kept in case of traceback
source_object.cache.batch_stackptr = ptr + 1
#show_curr(source_object)
source_object.execute_cmd(cmd)
else:
#show_curr(source_object)
source_object.execute_cmd(commands[ptr])
def reload_stack(source_object):
"reload the stack"
commands = parse_batchbuild_file(source_object.cache.batch_filename)
if commands:
ptr = source_object.cache.batch_stackptr
else:
source_object.emit_to("Commands in file could not be reloaded. Was it moved?")
def move_in_stack(source_object, step=1):
"store data in stack"
N = len(source_object.cache.batch_cmdstack)
currpos = source_object.cache.batch_stackptr
source_object.cache.batch_stackptr = max(0,min(N-1,currpos+step))
def exit_state(source_object):
"Quit the state"
source_object.cache.batch_cmdstack = None
source_object.cache.batch_stackptr = None
source_object.cache.batch_filename = None
# since clear_state() is protected against exiting the interactive mode
# (to avoid accidental drop-outs by rooms clearing a player's state),
# we have to clear the state directly here.
source_object.cache.state = None
def cmd_state_ll(command):
"""
ll
Look at the full source for the current
command definition.
"""
show_curr(command.source_object,showall=True)
def cmd_state_pp(command):
"""
pp
Process the currently shown command definition.
"""
process_commands(command.source_object)
def cmd_state_rr(command):
"""
rr
Reload the batch file, keeping the current
position in it.
"""
reload_stack(command.source_object)
command.source_object.emit_to("\nFile reloaded. Staying on same command.\n")
show_curr(command.source_object)
def cmd_state_rrr(command):
"""
rrr
Reload the batch file, starting over
from the beginning.
"""
reload_stack(command.source_object)
command.source_object.cache.batch_stackptr = 0
command.source_object.emit_to("\nFile reloaded. Restarting from top.\n")
show_curr(command.source_object)
def cmd_state_nn(command):
"""
nn
Go to next command. No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_nl(command):
"""
nl
Go to next command, viewing its full source.
No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_bb(command):
"""
bb
Backwards to previous command. No commands
are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = -int(command.command_argument)
else:
step = -1
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_bl(command):
"""
bl
Backwards to previous command, viewing its full
source. No commands are executed.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = -int(command.command_argument)
else:
step = -1
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_ss(command):
"""
ss [steps]
Process current command, then step to the next
one. If steps is given,
process this many commands.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
process_commands(source_object,step)
show_curr(source_object)
def cmd_state_sl(command):
"""
sl [steps]
Process current command, then step to the next
one, viewing its full source. If steps is given,
process this many commands.
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
step = int(command.command_argument)
else:
step = 1
process_commands(source_object,step)
show_curr(source_object, showall=True)
def cmd_state_cc(command):
"""
cc
Continue to process all remaining
commands.
"""
source_object = command.source_object
N = len(source_object.cache.batch_cmdstack)
ptr = source_object.cache.batch_stackptr
step = N - ptr
process_commands(source_object,step)
exit_state(source_object)
source_object.emit_to("Finished processing batch file.")
def cmd_state_jj(command):
"""
j <command number>
Jump to specific command number
"""
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
no = int(command.command_argument)-1
else:
source_object.emit_to("You must give a number index.")
return
ptr = source_object.cache.batch_stackptr
step = no - ptr
move_in_stack(source_object, step)
show_curr(source_object)
def cmd_state_jl(command):
"""
jl <command number>
Jump to specific command number and view its full source.
"""
global STACKPTRS
source_object = command.source_object
arg = command.command_argument
if arg and arg.isdigit():
no = int(command.command_argument)-1
else:
source_object.emit_to("You must give a number index.")
return
ptr = source_object.cache.batch_stackptr
step = no - ptr
move_in_stack(source_object, step)
show_curr(source_object, showall=True)
def cmd_state_qq(command):
"""
qq
Quit the batchprocessor.
"""
exit_state(command.source_object)
command.source_object.emit_to("Aborted interactive batch mode.")
def cmd_state_hh(command):
"Help command"
s = """
Interactive batch processing commands:
nn [steps] - next command (no processing)
nl [steps] - next & look
bb [steps] - back to previous command (no processing)
bl [steps] - back & look
jj <N> - jump to command nr N (no processing)
jl <N> - jump & look
pp - process currently shown command (no step)
ss [steps] - process & step
sl [steps] - process & step & look
ll - look at full definition of current command
rr - reload batch file (stay on current)
rrr - reload batch file (start from first)
hh - this help list
cc - continue processing to end, then quit.
qq - quit (abort all remaining commands)
"""
command.source_object.emit_to(s)
#create the state; we want it as open as possible so we can do everything
# in our batch processing.
GLOBAL_STATE_TABLE.add_state(STATENAME,global_cmds='all',
allow_exits=True,allow_obj_cmds=True,exit_command=True)
#add state commands
GLOBAL_STATE_TABLE.add_command(STATENAME,"nn",cmd_state_nn)
GLOBAL_STATE_TABLE.add_command(STATENAME,"nl",cmd_state_nl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"bb",cmd_state_bb)
GLOBAL_STATE_TABLE.add_command(STATENAME,"bl",cmd_state_bl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"jj",cmd_state_jj)
GLOBAL_STATE_TABLE.add_command(STATENAME,"jl",cmd_state_jl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"pp",cmd_state_pp)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ss",cmd_state_ss)
GLOBAL_STATE_TABLE.add_command(STATENAME,"sl",cmd_state_sl)
GLOBAL_STATE_TABLE.add_command(STATENAME,"cc",cmd_state_cc)
GLOBAL_STATE_TABLE.add_command(STATENAME,"ll",cmd_state_ll)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rr",cmd_state_rr)
GLOBAL_STATE_TABLE.add_command(STATENAME,"rrr",cmd_state_rrr)
GLOBAL_STATE_TABLE.add_command(STATENAME,"hh",cmd_state_hh)
GLOBAL_STATE_TABLE.add_command(STATENAME,"qq",cmd_state_qq)

364
src/commands/cmdhandler.py Normal file
View file

@ -0,0 +1,364 @@
"""
Command handler
This module contains the infrastructure for accepting commands on the
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) Depending on the login/not state, it collects cmdsets from different sources:
not logged in - uses the single cmdset in settings.CMDSET_UNLOGGEDIN
normal - gathers command sets from many different sources (shown in 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.
exits - exits from a room are dynamically made into a cmdset for matching,
allowing the player to give just the name and thus traverse the exit.
If a match, the traversing is handled by the CMD_EXIT system command.
object cmdsets - all objects at caller's location are scanned for non-empty
cmdsets.
caller - the caller is searched for its currently active cmdset.
4) All the gathered cmdsets (if more than one) are merged into one using the cmdset priority rules.
5) If no cmdsets where found, we raise NoCmdSet exception. This should not happen, at least the
caller should have a default cmdset available at all times. --> Finished
6) The raw input string is parsed using the parser defined by settings.CMDPARSER. It returns
a special match object since a command may consist of many space-separated words and we
thus have to match them all.
7) If no command was supplied, we search the merged cmdset for system command CMD_NOINPUT
and branches to execute that. --> Finished
8) We match the the match object against the merged cmdset and the eventual priorities given it
by the parser. The result is a list of command matches tied to their respective match object.
9) If we found no matches, branch to system command CMD_NOMATCH --> Finished
10) If we were unable to weed out multiple matches, branch CMD_MULTIMATCH --> Finished
11) If we have a single match, we now check user permissions.
not permissions: branch to system command CMD_NOPERM --> Finished
12) We analyze the matched command to determine if it is a channel-type command, that is
a command auto-created to represent a valid comm channel. If so, we see if CMD_CHANNEL is
custom-defined in the merged cmdset, or we launch the auto-created command
direclty --> Finished
13 We next check if this is an exit-type command, that is, a command auto-created to represent
an exit from this room. If so we check for custom CMD_EXIT in cmdset or launch
the auto-generated command directly --> Finished
14) 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.
When launching the command (normal, or system command both), two hook functions are called
in sequence, cmd.parse() followed by cmd.func(). It's up to the implementation as to how to
use this to most advantage.
"""
from traceback import format_exc
from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER
from src.commands.cmdsethandler import import_cmdset
from src.objects.exithandler import EXITHANDLER
from src.utils import logger
#This switches the command parser to a user-defined one.
# You have to restart the server for this to take effect.
try:
CMDPARSER = __import__(settings.ALTERNATE_PARSER, fromlist=[True]).cmdparser
except Exception:
from src.commands.cmdparser import cmdparser as CMDPARSER
# 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!
CMD_NOINPUT = "__noinput_command"
CMD_NOMATCH = "__nomatch_command"
CMD_MULTIMATCH = "__multimatch_command"
CMD_NOPERM = "__noperm_command"
CMD_CHANNEL = "__send_to_channel"
CMD_EXIT = "__move_to_exit"
class NoCmdSets(Exception):
"No cmdsets found. Critical error."
pass
class ExecSystemCommand(Exception):
"Run a system command"
def __init__(self, syscmd, sysarg):
self.args = (syscmd, sysarg) # needed by exception error handling
self.syscmd = syscmd
self.sysarg = sysarg
def get_and_merge_cmdsets(caller):
"""
Gather all relevant cmdsets and merge them. Note
that this is only relevant for logged-in callers.
"""
# The calling object's cmdset
try:
caller_cmdset = caller.cmdset.current
except AttributeError:
caller_cmdset = None
# All surrounding cmdsets
channel_cmdset = None
exit_cmdset = None
local_objects_cmdsets = [None]
#print "cmdset flags:", caller_cmdset.no_channels, caller_cmdset.no_exits, caller_cmdset.no_objs
if not caller_cmdset.no_channels:
# Make cmdsets out of all valid channels
channel_cmdset = CHANNELHANDLER.get_cmdset(caller)
if not caller_cmdset.no_exits:
# Make cmdsets out of all valid exits in the room
exit_cmdset = EXITHANDLER.get_cmdset(caller)
location = caller.location
if location and not caller_cmdset.no_objs:
# Gather all cmdsets stored on objects in the room
local_objlist = location.contents
local_objects_cmdsets = [obj.cmdset.current
for obj in local_objlist
if obj.cmdset.outside_access
and obj.cmdset.allow_outside_access(caller)]
# Merge all command sets into one
# (the order matters, the higher-prio cmdsets are merged last)
cmdset = caller_cmdset
for obj_cmdset in local_objects_cmdsets:
try:
cmdset = obj_cmdset + cmdset
except TypeError:
pass
try:
cmdset = exit_cmdset + cmdset
except TypeError:
pass
try:
cmdset = channel_cmdset + cmdset
except TypeError:
pass
return cmdset
def match_command(cmd_candidates, cmdset, logged_caller=None):
"""
Try to match the command against one of the
cmd_candidates.
logged_caller - a logged-in object, if any.
"""
# Searching possible command matches in the given cmdset
matches = []
prev_found_cmds = [] # to avoid aliases clashing with themselves
for cmd_candidate in cmd_candidates:
cmdmatches = list(set([cmd for cmd in cmdset
if cmd == cmd_candidate.cmdname and
cmd not in prev_found_cmds]))
matches.extend([(cmd_candidate, cmd) for cmd in cmdmatches])
prev_found_cmds.extend(cmdmatches)
if not matches or len(matches) == 1:
return matches
# Do our damndest to resolve multiple matches
# First try candidate priority to separate them
top_ranked = []
top_priority = None
for match in matches:
if top_priority == None \
or match[0].priority >= top_priority:
top_priority = match[0].priority
top_ranked.append(match)
matches = top_ranked
if not matches or len(matches) == 1:
return matches
# still multiplies. Check if player supplied
# an obj name on the command line. We know they
# all have at least the same cmdname and obj_key
# at this point.
if logged_caller:
try:
local_objlist = logged_caller.location.contents
match = matches[0]
top_ranked = [obj for obj in local_objlist
if match[0].obj_key == obj.name
and any(cmd == match[0].cmdname
for cmd in obj.cmdset.current)]
if top_ranked:
matches = \
[(match[0],
obj.cmdset.current.get(match[0].cmdname))
for obj in top_ranked]
except Exception:
logger.log_trace()
# regardless what we have at this point, we have to be content
return matches
# Main command-handler function
def cmdhandler(caller, raw_string, unloggedin=False):
"""
This is the main function to handle any string sent to the engine.
"""
try: # catch bugs in cmdhandler itself
try: # catch special-type commands
if unloggedin:
# not logged in, so it's just one cmdset we are interested in
cmdset = import_cmdset(settings.CMDSET_UNLOGGEDIN, caller)
else:
# We are logged in, collect all relevant cmdsets and merge
cmdset = get_and_merge_cmdsets(caller)
#print cmdset
if not cmdset:
# this is bad and shouldn't happen.
raise NoCmdSets
raw_string = raw_string.strip()
if not raw_string:
# Empty input. Test for system command instead.
syscmd = cmdset.get(CMD_NOINPUT)
sysarg = ""
raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string into command candidates
cmd_candidates = CMDPARSER(raw_string)
#string ="Command candidates"
#for cand in cmd_candidates:
# string += "\n %s || %s" % (cand.cmdname, cand.args)
#caller.msg(string)
# Try to produce a unique match between the merged
# cmdset and the candidates.
if unloggedin:
matches = match_command(cmd_candidates, cmdset)
else:
matches = match_command(cmd_candidates, cmdset, caller)
#print "matches: ", matches
# Deal with matches
if not matches:
# No commands match our entered command
syscmd = cmdset.get(CMD_NOMATCH)
if syscmd:
sysarg = raw_string
else:
sysarg = "Huh? (Type \"help\" for help)"
raise ExecSystemCommand(syscmd, sysarg)
if len(matches) > 1:
# We have a multiple-match
syscmd = cmdset.get(CMD_MULTIMATCH)
matchstring = ", ".join([match[0].cmdname
for match in matches])
if syscmd:
sysarg = matchstring
else:
sysarg = "There were multiple matches:\n %s"
sysarg = sysarg % matchstring
raise ExecSystemCommand(syscmd, sysarg)
# At this point, we have a unique command match.
cmd_candidate, cmd = matches[0]
# Check so we have permission to use this command.
if not cmd.has_perm(caller):
cmd = cmdset.get(CMD_NOPERM)
if cmd:
sysarg = raw_string
else:
sysarg = "Huh? (type 'help' for help)"
raise ExecSystemCommand(cmd, sysarg)
# 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 = cmdset.get(CMD_CHANNEL)
if syscmd:
# replace system command with custom version
cmd = syscmd
sysarg = "%s:%s" % (cmd_candidate.cmdname,
cmd_candidate.args)
raise ExecSystemCommand(cmd, sysarg)
# Check if this is an Exit match.
if hasattr(cmd, 'is_exit') and cmd.is_exit:
# 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_EXIT)
if syscmd:
# replace system command with custom version
cmd = syscmd
sysarg = raw_string
raise ExecSystemCommand(cmd, sysarg)
# A normal command.
# Assign useful variables to the instance
cmd.caller = caller
cmd.cmdstring = cmd_candidate.cmdname
cmd.args = cmd_candidate.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.
cmd.obj.scripts.validate()
# Parse and execute
cmd.parse()
cmd.func()
# Done!
except ExecSystemCommand, exc:
# Not a normal command: run a system command, if available,
# or fall back to a return string.
syscmd = exc.syscmd
sysarg = exc.sysarg
if syscmd:
syscmd.caller = caller
syscmd.cmdstring = syscmd.key
syscmd.args = sysarg
syscmd.cmdset = cmdset
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
# cmd.obj is automatically made available.
# we make sure to validate its scripts.
cmd.obj.scripts.validate()
# parse and run the command
syscmd.parse()
syscmd.func()
elif sysarg:
caller.msg(exc.sysarg)
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 += "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 += " Please file a bug report."
logger.log_trace(string)
caller.msg(string % format_exc())
except Exception:
# This catches exceptions in cmdhandler exceptions themselves
string = "%s\nAbove traceback is from a Command handler bug."
string += " Please contact an admin."
logger.log_trace(string)
caller.msg(string % format_exc())
#----------------------------------------------------- end cmdhandler

174
src/commands/cmdparser.py Normal file
View file

@ -0,0 +1,174 @@
"""
The default command parser. Use your own by assigning
settings.ALTERNATE_PARSER to a Python path to a module containing the
replacing cmdparser function. The replacement parser must
return a CommandCandidates object.
"""
import re
from django.conf import settings
# This defines how many space-separated words may at most be in a command.
COMMAND_MAXLEN = settings.COMMAND_MAXLEN
# These chars (and space) end a command name and may
# thus never be part of a command name. Exception is
# if the char is the very first character - the char
# is then treated as the name of the command.
SPECIAL_CHARS = ["/", "\\", "'", '"', ":", ";", "\-", '#', '=', '!']
# Pre-compiling the regular expression is more effective
REGEX = re.compile(r"""["%s"]""" % ("".join(SPECIAL_CHARS)))
class CommandCandidate(object):
"""
This is a convenient container for one possible
combination of command names that may appear if we allow
many-word commands.
"""
def __init__(self, cmdname, args=0, priority=0, obj_key=None):
"initiate"
self.cmdname = cmdname
self.args = args
self.priority = priority
self.obj_key = obj_key
def __str__(self):
return "<cmdname:'%s',args:'%s'>" % (self.cmdname, self.args)
#
# The command parser
#
def cmdparser(raw_string):
"""
This function parses the raw string into three parts: command
name(s), keywords(if any) and arguments(if any). It returns a
CommandCandidates object. It should be general enough for most
game implementations, but you can also overwrite it should you
wish to implement some completely different way of handling and
ranking commands. Arguments and keywords are parsed/dealt with by
each individual command's parse() command.
The cmdparser understand the following command combinations (where
[] marks optional parts and <char> is one of the SPECIAL_CHARs
defined globally.):
[<char>]cmdname[ cmdname2 cmdname3 ...][<char>] [the rest]
A command may contain spaces, but never any of of the <char>s. A
command can maximum have CMD_MAXLEN words, or the number of words
up to the first <char>, whichever is smallest. An exception is if
<char> is the very first character in the string - the <char> is
then assumed to be the actual command name (a common use for this
is for e.g ':' to be a shortcut for 'emote').
All words not part of the command name is considered a part of the
command's argument. Note that <char>s ending a command are never
removed but are included as the first character in the
argument. This makes it easier for individual commands to identify
things like switches. Example: '@create/drop ball' finds the
command name to trivially be '@create' since '/' ends it. As the
command's arguments are sent '/drop ball'. In this MUX-inspired
example, '/' denotes a keyword (or switch) and it is now easy for
the receiving command to parse /drop as a keyword just by looking
at the first character.
Allowing multiple command names means we have to take care of all
possible meanings and the result will be a CommandCandidates
object with up to COMMAND_MAXLEN names stored in it. So if
COMMAND_MAXLEN was, say, 4, we would have to search all commands
matching one of 'hit', 'hit orc', 'hit orc with' and 'hit orc with
sword' - each which are potentially valid commands. Assuming a
longer written name means being more specific, a longer command
name takes precedence over a short one.
There is one optional form:
<objname>'s [<char>]cmdname[ cmdname2 cmdname3 ...][<char>] [the rest]
This is to be used for object command sets with the 'duplicate' flag
set. It allows the player to define a particular object by name.
This object name(without the 's) will be stored as obj_key in the
CommandCandidates object and one version of the command name will be added
that lack this first part. If a command exists that has the same
name (including the 's), that command will be used
instead. Observe that the player setting <objname> will not override
normal commandset priorities - it's only used if there is no other
way to differentiate between commands (e.g. two objects in the
room both having the exact same command names and priorities).
"""
def produce_candidates(nr_candidates, wordlist):
"Helper function"
candidates = []
cmdwords_list = []
#print "wordlist:",wordlist
for n_words in range(nr_candidates):
cmdwords_list.append(wordlist.pop(0))
cmdwords = " ".join([word.strip().lower()
for word in cmdwords_list])
args = ""
for word in wordlist:
if not args or (word and (REGEX.search(word[0]))):
#print "nospace: %s '%s'" % (args, word)
args += word
else:
#print "space: %s '%s'" % (args, word)
args += " %s" % word
#print "'%s' | '%s'" % (cmdwords, args)
candidates.append(CommandCandidate(cmdwords, args, priority=n_words))
return candidates
raw_string = raw_string.strip()
#TODO: check for non-standard characters.
candidates = []
regex_result = REGEX.search(raw_string)
if not regex_result == None:
# there are characters from SPECIAL_CHARS in the string.
# since they cannot be part of a longer command, these
# will cut short the command, no matter how long we
# allow commands to be.
end_index = regex_result.start()
end_char = raw_string[end_index]
if end_index == 0:
# There is one exception: if the input begins with
# a special char, we let that be the command name.
cmdwords = end_char
if len(raw_string) > 1:
args = raw_string[1:]
else:
args = ""
candidates.append(CommandCandidate(cmdwords, args))
return candidates
else:
# the special char occurred somewhere inside the string
if end_char == "'" and \
len(raw_string) > end_index+1 and \
raw_string[end_index+1:end_index+3] == "s ":
# The command is of the form "<word>'s ". The
# player might have made an attempt at identifying the
# object of which's cmdtable we should prefer (e.g.
# > red door's button).
obj_key = raw_string[:end_index]
alt_string = raw_string[end_index+2:]
alt_candidates = cmdparser(alt_string)
for candidate in alt_candidates:
candidate.obj_key = obj_key
candidates.extend(alt_candidates)
# now we let the parser continue as normal, in case
# the 's -business was not meant to be an obj ref at all.
# We only run the command finder up until the end char
nr_candidates = len(raw_string[:end_index].split(None))
if nr_candidates <= COMMAND_MAXLEN:
wordlist = raw_string[:end_index].split(" ")
wordlist.extend(raw_string[end_index:].split(" "))
#print "%i, wordlist: %s" % (nr_candidates, wordlist)
candidates.extend(produce_candidates(nr_candidates, wordlist))
return candidates
# if there were no special characters, or that character
# was not found within the allowed number of words, we run normally
nr_candidates = min(COMMAND_MAXLEN,
len(raw_string.split(None)))
wordlist = raw_string.split(" ")
candidates.extend(produce_candidates(nr_candidates, wordlist))
return candidates

338
src/commands/cmdset.py Normal file
View file

@ -0,0 +1,338 @@
"""
A cmdset holds a set of commands available to the object or to other
objects near it. All the commands a player can give (look, @create etc)
are stored as the default cmdset on the player object and managed using the
CmdHandler object (see cmdhandler.py).
The power of having command sets in CmdSets like this is that CmdSets
can be merged together according to individual rules to create a new
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
together to create interesting in-game effects.
"""
import copy
class CmdSetMeta(type):
"""
This metaclass makes some minor on-the-fly convenience fixes to
the cmdset class.
"""
def __init__(mcs, *args, **kwargs):
"""
Fixes some things in the cmdclass
"""
# by default we key the cmdset the same as the
# name of its class.
mcs.key = mcs.__name__
if not type(mcs.key_mergetypes) == dict:
mcs.key_mergetypes = {}
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
# Some priority-sensitive merge operations for cmdsets
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[:]
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
def intersect(cmdset_a, cmdset_b, duplicates=False):
"C = A (intersect) B. A is assumed higher priority"
cmdset_c = cmdset_a.copy_this()
if duplicates and cmdset_a.priority == cmdset_b.priority:
for cmd in [cmd for cmd in cmdset_a if cmd in cmdset_b]:
cmdset_c.add(cmd)
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
def replace(cmdset_a, cmdset_b, cmdset_c):
"C = A + B where the result is A."
cmdset_c = cmdset_a.copy_this()
cmdset_c.commands = cmdset_a.commands[:]
return cmdset_c
def remove(cmdset_a, cmdset_b, cmdset_c):
"C = A + B, where B is filtered by A"
cmdset_c = cmdset_a.copy_this()
cmdset_c.commands = [cmd for cmd in cmdset_b if not cmd in cmdset_a]
return cmdset_c
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.
"""
if callable(cmd):
# this is a valid check since Command *instances*
# don't implement __call__, so this will catch
# Command *classes* and instantiate them.
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
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
merged cmdset. Same-name commands are merged by
priority. This is the most common default.
Ex: A1,A3 + B1,B2,B4,B5 = A1,B2,A3,B4,B5
Intersect - Only commands found in *both* cmdsets
(i.e. which have same names) end up in the merged
cmdset, with the higher-priority cmdset replacing the
lower one. Ex: A1,A3 + B1,B2,B4,B5 = A1
Replace - The commands of this cmdset completely replaces
the lower-priority cmdset's commands, regardless
of if same-name commands exist.
Ex: A1,A3 + B1,B2,B4,B5 = A1,A3
Remove - This removes the relevant commands from the
lower-priority cmdset completely. They are not
replaced with anything, so this in effects uses the
high-priority cmdset as a filter to affect the
low-priority cmdset.
Ex: A1,A3 + B1,B2,B4,B5 = B2,B4,B5
Note: Commands longer than 2 characters and starting
with double underscrores, like '__noinput_command'
are considered 'system commands' and are
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. Evennia uses priorities from 0-10
where 10 are used for high-priority things like comsys
channel names and 9 for exit names in order to give
these priority when the given command matches.
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
precedence. But if allow_duplicates is true, the
result will be a merger with more than one of each
name match. This will usually lead to the player
receiving a multiple-match error higher up the road,
but can be good for things like cmdsets on non-player
objects in a room, to allow the system to warn that
more than one 'ball' in the room has the same 'kick'
command defined on it, so it may offer a chance to
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.
Example: {'Myevilcmdset','Replace'} which would make
sure for this set to always use 'Replace' on
Myevilcmdset no matter what overall mergetype this set
has.
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)
"""
__metaclass__ = CmdSetMeta
key = "Unnamed CmdSet"
mergetype = "Union"
priority = 0
duplicates = False
key_mergetypes = {}
no_exits = False
no_objs = False
no_channels = 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
regular object.
"""
if key:
self.key = key
self.commands = []
self.actual_mergetype = self.mergetype
self.cmdsetobj = cmdsetobj
# initialize system
self.at_cmdset_creation()
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):
"""
Add a command to this cmdset.
Note that if cmd already has
"""
cmd = instantiate(cmd)
if cmd:
if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj
self.commands.append(cmd)
self.commands = list(set(self.commands))
def remove(self, cmd):
"""
Remove a command instance from the cmdset.
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.
"""
cmd = instantiate(cmd)
if cmd:
for thiscmd in self.commands:
if thiscmd == cmd:
return thiscmd
def get_system_cmds(self):
"""
Return system commands in the cmdset, defined as
commands starting with double underscore __.
These are excempt from merge operations.
"""
return [cmd for cmd in self.commands
if len(cmd.key) > 2 and cmd.key[:2] == '__']
def copy_this(self):
"""
Returns a new cmdset with the same settings as this one
(no commands are copied over)
"""
cmdset = CmdSet()
cmdset.key = self.key
cmdset.cmdsetobj = self.cmdsetobj
cmdset.no_exits = self.no_exits
cmdset.no_objs = self.no_objs
cmdset.no_channels = self.no_channels
cmdset.mergetype = self.mergetype
cmdset.priority = self.priority
cmdset.duplicates = self.duplicates
cmdset.key_mergetypes = copy.deepcopy(self.key_mergetypes)
return cmdset
def __str__(self):
"""
Show all commands in cmdset when printing it.
"""
return ", ".join([str(cmd) for cmd in self.commands])
def __iter__(self):
"""
Allows for things like 'for cmd in cmdset':
"""
return iter(self.commands)
def __contains__(self, othercmd):
"""
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
Here, we (by convention) say that 'A is merged onto B to form
C'. The actual merge operation used in the 'addition' depends
on which priorities A and B have. The one of the two with the
highest priority will apply and give its properties to C. In
the case of a tie, A takes priority and replaces the
same-named commands in B unless A has the 'duplicate' variable
set (which means both sets' commands are kept).
"""
# It's okay to merge with None
if not cmdset_b:
return self
# preserve system __commands
sys_commands = self.get_system_cmds() + cmdset_b.get_system_cmds()
if self.priority >= cmdset_b.priority:
# A higher or equal priority than B
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":
cmdset_c = replace(self, cmdset_b, cmdset_b.duplicates)
elif mergetype == "Remove":
cmdset_c = remove(self, cmdset_b, cmdset_b.duplicates)
else: # Union
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)
if mergetype == "Intersect":
cmdset_c = intersect(cmdset_b, self, self.duplicates)
elif mergetype == "Replace":
cmdset_c = replace(cmdset_b, self, self.duplicates)
elif mergetype == "Remove":
cmdset_c = remove(self, cmdset_b, self.duplicates)
else: # Union
cmdset_c = union(cmdset_b, self, self.duplicates)
# we store actual_mergetype since key_mergetypes
# might be different from the main mergetype.
# This is used for diagnosis.
cmdset_c.actual_mergetype = mergetype
# return the system commands to the cmdset
for cmd in sys_commands:
cmdset_c.add(cmd)
return cmdset_c

View file

@ -0,0 +1,376 @@
"""
CmdSethandler
The Cmdhandler tracks an object's 'Current CmdSet', which is the
current merged sum of all CmdSets added to it.
A CmdSet constitues a set of commands. The CmdSet works as a special
intelligent container that, when added to other CmdSet make sure that
same-name commands are treated correctly (usually so there are no
doublets). This temporary but up-to-date merger of CmdSet is jointly
called the Current Cmset. It is this Current CmdSet that the
commandhandler looks through whenever a player enters a command (it
also adds CmdSets from objects in the room in real-time). All player
objects have a 'default cmdset' containing all the normal in-game mud
commands (look etc).
So what is all this cmdset complexity good for?
In its simplest form, a CmdSet has no commands, only a key name. In
this case the cmdset's use is up to each individual game - it can be
used by an AI module for example (mobs in cmdset 'roam' move from room
to room, in cmdset 'attack' they enter combat with players).
Defining commands in cmdsets offer some further powerful game-design
consequences however. Here are some examples:
As mentioned above, all players always have at least the Default
CmdSet. This contains the set of all normal-use commands in-game,
stuff like look and @desc etc. Now assume our players end up in a dark
room. You don't want the player to be able to do much in that dark
room unless they light a candle. You could handle this by changing all
your normal commands to check if the player is in a dark room. This
rapidly goes unwieldly and error prone. Instead you just define a
cmdset with only those commands you want to be available in the 'dark'
cmdset - maybe a modified look command and a 'light candle' command -
and have this completely replace the default cmdset.
Another example: Say you want your players to be able to go
fishing. You could implement this as a 'fish' command that fails
whenever the player has no fishing rod. Easy enough. But what if you
want to make fishing more complex - maybe you want four-five different
commands for throwing your line, reeling in, etc? Most players won't
(we assume) have fishing gear, and having all those detailed commands
is cluttering up the command list. And what if you want to use the
'throw' command also for throwing rocks etc instead of 'using it up'
for a minor thing like fishing?
So instead you put all those detailed fishing commands into their own
CommandSet called 'Fishing'. Whenever the player gives the command
'fish' (presumably the code checks there is also water nearby), only
THEN this CommandSet is added to the Cmdhandler of the player. The
'throw' command (which normally throws rocks) is replaced by the
custom 'fishing variant' of throw. What has happened is that the
Fishing CommandSet was merged on top of the Default ones, and due to
how we defined it, its command overrules the default ones.
When we are tired of fishing, we give the 'go home' command (or
whatever) and the Cmdhandler simply removes the fishing CommandSet
so that we are back at defaults (and can throw rocks again).
Since any number of ComandSets can be piled on top of each other, you
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
from src.utils import logger
from src.utils import create
from src.commands.cmdset import CmdSet
from src.scripts.scripts import AddCmdSet
CACHED_CMDSETS = {}
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.
cmsetobj - 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.
function returns None if an error was encountered or path not found.
"""
try:
try:
wanted_cache_key = python_path
cmdsetclass = CACHED_CMDSETS.get(wanted_cache_key, None)
errstring = ""
if not cmdsetclass:
#print "cmdset %s not in cache. Reloading." % wanted_cache_key
# 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
#print "cmdset %s found." % wanted_cache_key
#instantiate the cmdset (and catch its errors)
if callable(cmdsetclass):
cmdsetclass = cmdsetclass(cmdsetobj)
return cmdsetclass
except ImportError:
errstring = "Error loading cmdset: Couldn't import module '%s'."
errstring = errstring % modulepath
raise
except KeyError:
errstring = "Error in loading cmdset: No cmdset class '%s' in %s."
errstring = errstring % (modulepath, classname)
raise
except Exception:
errstring = "\n%s\nCompile/Run error when loading cmdset '%s'."
errstring = errstring % (traceback.format_exc(), python_path)
raise
except Exception:
if errstring and not no_logging:
logger.log_trace()
if emit_to_obj:
emit_to_obj.msg(errstring)
raise
def get_cached_cmdsets():
"""
Get the currently live cache from outside this module.
Calling a routine avoids update-problems when importing
the variable from an external module.
"""
return CACHED_CMDSETS
# classes
class CmdSetHandler(object):
"""
The CmdSetHandler is always stored on an object, supplied as the argument.
The 'current' cmdset is the merged set currently active for this 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.
"""
def __init__(self, obj, outside_access=True):
"""
This method is called whenever an object is recreated.
obj - this is a reference to the game object this handler
belongs to.
outside_access - if false, the cmdparser will only retrieve
this cmdset when it is its obj itself that is calling for it.
(this is is good to use for player objects, since they
should not have access to the cmdsets of other player
objects).
"""
self.obj = obj
# if false, only the object itself may use this handler
# (this should be set especially by character objects)
self.outside_access = outside_access
# the id of the "merged" current cmdset for easy access.
self.key = None
# 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.update()
def __str__(self):
"Display current commands"
string = ""
if len(self.cmdset_stack) > 1:
# We have more than one cmdset in stack; list them all
num = 0
#print self.cmdset_stack, self.mergetype_stack
for snum, cmdset in enumerate(self.cmdset_stack):
num = snum
mergetype = self.mergetype_stack[snum]
if mergetype != cmdset.mergetype:
mergetype = "%s^" % (mergetype)
string += "\n %i: <%s (%s, prio %i)>: %s" % \
(snum, cmdset.key, mergetype,
cmdset.priority, cmdset)
string += "\n (combining %i cmdsets):" % (num+1)
else:
string += "\n "
# Display the currently active cmdset
mergetype = self.mergetype_stack[-1]
if mergetype != self.current.mergetype:
merged_on = self.cmdset_stack[-2].key
mergetype = "custom %s on %s" % (mergetype, merged_on)
string += " <%s (%s)> %s" % (self.current.key,
mergetype, self.current)
return string.strip()
def allow_outside_access(self, source_object):
"""
This is what the main commandhandler is using to check if
it should include this handler when searching for matching
commands. It should return True for most of the time except
for player-object handlers, which are only available to the
player herself. Handle individual permission checks with
the command.permissions mechanic instead.
"""
return self.outside_access or self.obj == source_object
def update(self):
"""
Re-adds all sets in the handler to have an updated
current set.
"""
updated = None
self.mergetype_stack = []
for cmdset in self.cmdset_stack:
try:
# for cmdset's '+' operator, order matters.
updated = cmdset + updated
except TypeError:
continue
self.mergetype_stack.append(updated.actual_mergetype)
self.current = updated
def import_cmdset(self, cmdset_path, emit_to_obj=None):
"""
load a cmdset from a module.
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. no script
will be added to add the cmdset every server start/login).
cmdset - can be a cmdset object or the python path to
such an object.
emit_to_obj - an object to receive error messages.
permanent - create a script to automatically add the cmdset
every time the server starts/the object logins.
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).
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.
"""
if callable(cmdset):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset, emit_to_obj)
if cmdset:
self.cmdset_stack.append(cmdset)
self.update()
if permanent:
# create a script to automatically add this cmdset at
# startup. We don't start it here since the cmdset was
# already added above.
try:
cmdset = "%s.%s" % (cmdset.__module__, cmdset.__name__)
except Exception:
logger.log_trace()
return
script = create.create_script(AddCmdSet)
script.db.cmdset = cmdset
script.db.add_default = False
self.obj.scripts.add(script, autostart=False)
def add_default(self, cmdset, emit_to_obj=None, permanent=False):
"""
Add a new default cmdset. If an old default existed,
it is replaced. If permanent is set, a script will be created to
add the cmdset to the object.
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.
permanent - create a script that assigns this script every
startup/login.
See also the notes for self.add(), which applies here too.
"""
if callable(cmdset):
cmdset = cmdset(self.obj)
elif isinstance(cmdset, basestring):
# this is (maybe) a python path. Try to import from cache.
cmdset = self.import_cmdset(cmdset, emit_to_obj)
if cmdset:
self.cmdset_stack[0] = cmdset
self.mergetype_stack[0] = cmdset.mergetype
self.update()
#print "add_default:", permanent
if permanent:
# create a script to automatically add this cmdset at
# startup. We don't start it here since the cmdset was
# already added above.
try:
cmdset = "%s.%s" % (cmdset.__module__, cmdset.__class__.__name__)
except Exception:
#print traceback.format_exc()
logger.log_trace()
return
#print "cmdset to add:", cmdset
script = create.create_script(AddCmdSet)
script.db.cmdset = cmdset
script.db.add_default = True
self.obj.scripts.add(script, key="add_default_cmdset", autostart=False)
def delete(self, key_or_class=None):
"""
Remove a cmdset from the handler. If a key is supplied,
it attempts to remove this. If no key is given,
the last cmdset in the stack is removed. Whenever
the cmdset_stack changes, the cmdset is updated.
The default cmdset (first entry in stack) is never
removed - set it to an empty set with add_default instead.
key_or_class - a specific cmdset key or a cmdset class (in
the latter case, *all* cmdsets of this class
will be removed from handler!)
"""
if len(self.cmdset_stack) < 2:
# don't allow deleting default cmdsets here.
return
if not key_or_class:
# remove the last one in the stack (except the default position)
self.cmdset_stack.pop()
else:
# argument key is given, is it a key or a class?
default_cmdset = self.cmdset_stack[0]
if callable(key_or_class) and hasattr(key_or_class, '__name__'):
# this is a callable with __name__ - we assume it's a class
self.cmdset_stack = [cmdset for cmdset in self.cmdset_stack[1:]
if cmdset.__class__.__name__ != key_or_class.__name__]
else:
# try it as a string
self.cmdset_stack = [cmdset for cmdset in self.cmdset_stack[1:]
if cmdset.key != key_or_class]
self.cmdset_stack.insert(0, default_cmdset)
# re-sync the cmdsethandler.
self.update()
def all(self):
"""
Returns the list of cmdsets. Mostly useful to check if stack if empty or not.
"""
return self.cmdset_stack
def clear(self):
"""
Removes all extra Command sets from the handler, leaving only the
default one.
"""
self.cmdset_stack = [self.cmdset_stack[0]]
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
self.update()

149
src/commands/command.py Normal file
View file

@ -0,0 +1,149 @@
"""
The base Command class.
All commands in Evennia inherit from the 'Command' class in this module.
"""
from src.permissions import permissions
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.
"""
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.
"""
mcs.key = mcs.key.lower()
if mcs.aliases and not is_iter(mcs.aliases):
mcs.aliases = mcs.aliases.split(',')
mcs.aliases = [str(alias).strip().lower() for alias in mcs.aliases]
if mcs.permissions and not is_iter(mcs.permissions) :
mcs.permissions = mcs.permissions.split(',')
mcs.permissions = [str(perm).strip().lower() for perm in mcs.permissions]
mcs.help_category = mcs.help_category.lower()
super(CommandMeta, mcs).__init__(*args, **kwargs)
# The Command class is the basic unit of an Evennia command; when
# defining new commands, the admin subclass this class and
# 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.
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
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
you to know which alias was used, for example)
cmd.args - everything supplied to the command following the cmdstring
(this is usually what is parsed in self.parse())
cmd.cmdset - the cmdset from which this command was matched (useful only
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.
(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)
"""
# Tie our metaclass, for some convenience cleanup
__metaclass__ = CommandMeta
# the main way to call this command (e.g. 'look')
key = "command"
# alternative ways to call the command (e.g. 'l', 'glance', 'examine')
aliases = []
# a list of permission strings or comma-separated string limiting
# access to this command.
permissions = []
# used by the help system to group commands in lists.
help_category = "default"
# 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.
def __str__(self):
"Print the command"
return self.key
def __eq__(self, cmd):
"""
Compare two command instances to each other by matching their
key and aliases.
input can be either a cmd object or the name of a command.
"""
if type(cmd) != self:
return self.match(cmd)
return self.match(cmd.key)
def __contains__(self, query):
"""
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.
input can be either a command object or a command name.
"""
if type(query) == type(Command()):
query = query.key
return (query in self.key) or \
(sum([query in alias for alias in self.aliases]) > 0)
def match(self, cmdname):
"""
This is called by the system when searching the available commands,
in order to determine if this is the one we wanted. cmdname was
previously extracted from the raw string by the system.
cmdname is always lowercase when reaching this point.
"""
return (cmdname == self.key) or (cmdname in self.aliases)
def has_perm(self, srcobj):
"""
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.
"""
return permissions.has_perm(srcobj, self, 'cmd')
# Common Command hooks
def parse(self):
"""
Once the cmdhandler has identified this as the command we
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
(notably self.args).
"""
pass
def func(self):
"""
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())
"""
string = "Command '%s' was executed with arg string '%s'."
self.caller.msg(string % (self.key, self.args))

View file

@ -1,623 +0,0 @@
"""
Comsys command module.
"""
from src import comsys
from src.channels.models import CommChannelMembership, CommChannel
from src import defines_global
from src.objects.models import Object
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_addcom(command):
"""
addcom - join a channel with alias
Usage:
addcom [alias=] <channel>
Allows adding an alias for a channel to make is easier and
faster to use. Subsequent calls of this command can
be used to add multiple aliases.
"""
source_object = command.source_object
command_argument = command.command_argument
if not command_argument:
source_object.emit_to("Usage: addcom [alias=]channelname.")
return
if '=' in command_argument:
chan_alias, chan_name = command.command_argument.split('=', 1)
chan_alias, chan_name = chan_alias.strip(), chan_name.strip()
else:
chan_name = command_argument.strip()
chan_alias = chan_name
membership = source_object.channel_membership_set.filter(channel__name__iexact=chan_name)
try:
chan = CommChannel.objects.get(name__iexact=chan_name)
s = ""
#if we happened to have this alias already defined on another channel, make
#sure to tell us.
aliasmatch = [cmatch.channel.name for cmatch in
source_object.channel_membership_set.filter(user_alias=chan_alias)
if cmatch.channel.name != chan_name]
if aliasmatch:
s = "The alias '%s' is already in use (for channel '%s')." % (chan_alias, aliasmatch[0])
source_object.emit_to(s)
return
if membership:
#we are already members of this channel. Set a different alias.
# Note: To this without requiring a the user to logout then login again,
# we need to delete, then rejoin the channel. Is this due to the lazy
# loading? /Griatch
prev_alias = membership[0].user_alias
if chan_alias == prev_alias:
s += "Alias unchanged."
else:
comsys.plr_del_channel(source_object, prev_alias)
comsys.plr_add_channel(source_object, chan_alias, chan)
s += "Channel '%s' alias changed from '%s' to '%s'." % (chan_name,prev_alias,
chan_alias)
else:
# This adds a CommChannelMembership object and a matching dict entry
# on the session's cdict.
comsys.plr_add_channel(source_object, chan_alias, chan)
# Let the player know everything went well.
s += "You join %s, with an alias of %s." % \
(chan.get_name(), chan_alias)
# Announce the user's joining.
join_msg = "%s has joined the channel." % \
(source_object.get_name(show_dbref=False),)
comsys.send_cmessage(chan, join_msg)
source_object.emit_to(s)
except CommChannel.DoesNotExist:
# Failed to match iexact on channel's 'name' attribute.
source_object.emit_to("Could not find channel %s." % chan_name)
GLOBAL_CMD_TABLE.add_command("addcom", cmd_addcom, help_category="Comms")
def cmd_delcom(command):
"""
delcom - remove a channel alias
Usage:
delcom <alias>
Removes the specified alias to a channel. If this is the last alias,
the user is effectively removed from the channel.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Usage: delcom <alias>")
return
try:
membership = source_object.channel_membership_set.get(user_alias__iexact=command.command_argument)
except CommChannelMembership.DoesNotExist:
source_object.emit_to("You are not on that channel.")
return
chan_name = membership.channel.get_name()
source_object.emit_to("You have left %s." % chan_name)
comsys.plr_del_channel(source_object, command.command_argument)
# Announce the user's leaving.
leave_msg = "%s has left the channel." % \
(source_object.get_name(show_dbref=False),)
comsys.send_cmessage(chan_name, leave_msg)
GLOBAL_CMD_TABLE.add_command("delcom", cmd_delcom,help_category="Comms")
def cmd_comlist(command):
"""
comlist - list channel memberships
Usage:
comlist
Lists the channels a user is subscribed to.
"""
source_object = command.source_object
session = command.session
s = "Your subscibed channels (use @clist for full chan list)\n"
s += "** Alias Channel Status\n"
channels = source_object.channel_membership_set.all()
if not channels:
s += " (No subscriptions) "
for membership in channels:
chan = membership.channel
if membership.is_listening:
chan_on = "On"
else:
chan_on = "Off"
s += " %s%s %-15.14s%-22.15s%s\n" % ('-','-',membership.user_alias,
chan.get_name(), chan_on)
s = s[:-1]
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("comlist", cmd_comlist,help_category="Comms")
def cmd_allcom(command):
"""
allcom - operate on all channels
Usage:
allcom [on | off | who | clear]
Allows the user to universally turn off or on all channels they are on,
as well as perform a 'who' for all channels they are on. Clear deletes
all channels.
Without argument, works like comlist.
"""
source_object = command.source_object
arg = command.command_argument
if not arg:
cmd_comlist(command)
source_object.emit_to("(allcom arguments: 'on', 'off', 'who' and 'clear'.)")
return
arg = arg.strip()
if arg == 'clear':
cmd_clearcom(command)
return
#get names and alias of all subscribed channels
chandict = comsys.plr_get_cdict(command.session)
aliaslist = chandict.keys()
aliaslist.sort()
if arg == "on":
for alias in aliaslist:
comsys.plr_chan_on(command.session, alias)
elif arg == "off":
for alias in aliaslist:
comsys.plr_chan_off(command.session, alias)
elif arg == "who":
s = ""
if not aliaslist:
s += " (No channels) "
for alias in aliaslist:
s += "-- %s (alias: %s)\n" % (chandict[alias][0],alias)
sess_list = comsys.get_cwho_list(chandict[alias][0])
objlist = [sess.get_pobject() for sess in sess_list]
plist = [p.get_name(show_dbref=source_object.sees_dbrefs())
for p in filter(lambda o: o.is_player(), objlist)]
olist = [o.get_name(show_dbref=source_object.sees_dbrefs())
for o in filter(lambda o: not o.is_player(), objlist)]
plist.sort()
olist.sort()
if plist:
s += " Players:\n "
for pname in plist:
s += "%s, " % pname
s = s[:-2] + "\n"
if olist:
s += " Objects:\n "
for oname in olist:
s += "%s, " % oname
s = s[:-2] + "\n"
s = s[:-1]
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("allcom", cmd_allcom, help_category="Comms")
def cmd_clearcom(command):
"""
clearcom - removes all channels
Usage:
clearcom
Effectively runs delcom on all channels the user is on. It will remove
their aliases, remove them from the channel, and clear any titles they
have set.
"""
source_object = command.source_object
#get aall subscribed channel memberships
memberships = source_object.channel_membership_set.all()
if not memberships:
s = "No channels to delete. "
else:
s = "Deleting all channels in your subscriptions ...\n"
for membership in memberships:
chan_name = membership.channel.get_name()
s += "You have left %s.\n" % chan_name
comsys.plr_del_channel(source_object, membership.user_alias)
comsys.send_cmessage(chan_name, "%s has left the channel." % source_object.get_name(show_dbref=False))
s = s[:-1]
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("clearcom", cmd_clearcom)
def cmd_clist(command):
"""
@clist
Usage:
@clist
Lists all available channels in the game.
[[clist]]
This is the same as @clist - it shows all
available channels in game.
"""
session = command.session
source_object = command.source_object
s = "All channels (use comlist or allcom to see your subscriptions)\n"
s += "** Channel Owner Description\n"
channels = comsys.get_all_channels()
if not channels:
s += "(No channels) "
for chan in channels:
s += " %s%s %-15.14s%-22.15s%s\n" % \
('-',
'-',
chan.get_name(),
chan.get_owner().get_name(show_dbref=False),
chan.description)
s = s[:-1]
#s += "** End of Channel List **"
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("@clist", cmd_clist, help_category="Comms")
GLOBAL_CMD_TABLE.add_command("clist", cmd_clist, help_category="Comms")
def cmd_cdestroy(command):
"""
@cdestroy
Usage:
@cdestroy <channel>
Destroys a channel that you control.
"""
source_object = command.source_object
cname = command.command_argument
if not cname:
source_object.emit_to("Usage: @cdestroy <channelname>")
return
name_matches = comsys.cname_search(cname, exact=True)
if not name_matches:
source_object.emit_to("Could not find channel %s." % (cname,))
else:
is_controlled_by_plr = name_matches[0].controlled_by(source_object)
if is_controlled_by_plr or source_object.has_perm("channels.channel_admin"):
source_object.emit_to("Channel %s destroyed." % (name_matches[0],))
name_matches.delete()
else:
source_object.emit_to("Permission denied.")
return
GLOBAL_CMD_TABLE.add_command("@cdestroy", cmd_cdestroy, help_category="Comms")
def cmd_cset(command):
"""
@cset
Sets various flags on a channel.
"""
# TODO: Implement cmd_cset
pass
def cmd_ccharge(command):
"""
@ccharge
Sets the cost to transmit over a channel. Default is free.
"""
# TODO: Implement cmd_ccharge
pass
def cmd_cboot(command):
"""
@cboot
Usage:
@cboot[/quiet] <channel> = <player or object>
Kicks a player or object from a channel you control.
"""
source_object = command.source_object
args = command.command_argument
switches = command.command_switches
if not args or not "=" in args:
source_object.emit_to("Usage: @cboot[/quiet] <channel> = <object>")
return
cname, objname = args.split("=",1)
cname, objname = cname.strip(), objname.strip()
if not cname or not objname:
source_object.emit_to("You must supply both channel and object.")
return
try:
channel = CommChannel.objects.get(name__iexact=cname)
except CommChannel.DoesNotExist:
source_object.emit_to("Could not find channel %s." % cname)
return
#do we have power over this channel?
if not channel.controlled_by(source_object) or source_object.has_perm("channels.channel_admin"):
source_object.emit_to("You don't have that power in channel '%s'." % cname)
return
#mux specification requires an * before player objects.
player_boot = False
if objname[0] == '*':
player_boot = True
objname = objname[1:]
bootobj = Object.objects.player_name_search(objname)
if not bootobj:
source_object.emit_to("Object '%s' not found." % objname)
return
if bootobj.is_player() and not player_boot:
source_object.emit_to("To boot players you need to start their name with an '*'. ")
return
#check so that this object really is on the channel in the first place
membership = bootobj.channel_membership_set.filter(channel__name__iexact=cname)
if not membership:
source_object.emit_to("'%s' is not on channel '%s'." % (objname,cname))
return
#announce to channel
if not 'quiet' in switches:
comsys.send_cmessage(cname, "%s boots %s from channel." % \
(source_object.get_name(show_dbref=False), objname))
#all is set, boot the object by removing all its aliases from the channel.
for mship in membership:
comsys.plr_del_channel(bootobj, mship.user_alias)
GLOBAL_CMD_TABLE.add_command("@cboot", cmd_cboot, help_category="Comms")
def cmd_cemit(command):
"""
@cemit - send a message to channel
Usage:
@cemit <channel>=<message>
@cemit/noheader <channel>=<message>
@cemit/sendername <channel>=<message>
Allows the user to send a message over a channel as long as
they own or control it. It does not show the user's name unless they
provide the /sendername switch.
[[channel_commands]]
Useful channel commands
(see their help pages for detailed help and options)
- Listing channels
clist - show all channels available to you
comlist - show channels you listen to
- Joining/parting channels
addcom - add your alias for a channel
delcom - remove alias for channel
(leave channel if no more aliases)
allcom - view, on/off or remove all your channels
clearcom - removes all channels
- Other
who - list who's online
<chanalias> off - silence channel temporarily
<chanalias> on - turn silenced channel back on
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("@cemit[/switches] <channel> = <message>")
return
eq_args = command.command_argument.split('=', 1)
if len(eq_args) != 2:
source_object.emit_to("You must provide a channel name and a message to emit.")
return
cname = eq_args[0].strip()
cmessage = eq_args[1].strip()
final_cmessage = cmessage
if len(cname) == 0:
source_object.emit_to("You must provide a channel name to emit to.")
return
if len(cmessage) == 0:
source_object.emit_to("You must provide a message to emit.")
return
name_matches = comsys.cname_search(cname, exact=True)
if name_matches:
cname_parsed = name_matches[0].get_name()
else:
source_object.emit_to("Could not find channel %s." % (cname,))
return
# If this is False, don't show the channel header before
# the message. For example: [Public] Woohoo!
show_channel_header = True
if "noheader" in command.command_switches:
if not source_object.has_perm("objects.emit_commchannel"):
source_object.emit_to(defines_global.NOPERMS_MSG)
return
final_cmessage = cmessage
show_channel_header = False
else:
if "sendername" in command.command_switches:
if not comsys.plr_has_channel(command.session, cname_parsed,
return_muted=False):
source_object.emit_to("You must be on %s to do that." % (cname_parsed,))
return
final_cmessage = "%s: %s" % (source_object.get_name(show_dbref=False),
cmessage)
else:
if not source_object.has_perm("objects.emit_commchannel"):
source_object.emit_to(defines_global.NOPERMS_MSG)
return
final_cmessage = cmessage
if not "quiet" in command.command_switches:
source_object.emit_to("Sent - %s" % (name_matches[0],))
comsys.send_cmessage(cname_parsed, final_cmessage,
show_header=show_channel_header)
#pipe to external channels (IRC, IMC) eventually mapped to this channel
comsys.send_cexternal(cname_parsed, cmessage, caller=source_object)
GLOBAL_CMD_TABLE.add_command("@cemit", cmd_cemit,priv_tuple=("channels.emit_commchannel",),
help_category="Comms")
def cmd_cwho(command):
"""
@cwho
Usage:
@cwho channel[/all]
Displays the name, status and object type for a given channel.
Adding /all after the channel name will list disconnected players
as well.
"""
session = command.session
source_object = command.source_object
if not command.command_argument:
cmd_clist(command)
source_object.emit_to("Usage: @cwho <channel>[/all]")
return
channel_name = command.command_argument
if channel_name.strip() == '':
source_object.emit_to("You must specify a channel name.")
return
name_matches = comsys.cname_search(channel_name, exact=True)
if name_matches:
# Check to make sure the user has permission to use @cwho.
is_channel_admin = source_object.has_perm("objects.channel_admin")
is_controlled_by_plr = name_matches[0].controlled_by(source_object)
if is_controlled_by_plr or is_channel_admin:
comsys.msg_cwho(source_object, channel_name)
else:
source_object.emit_to("Permission denied.")
return
else:
source_object.emit_to("No channel with that name was found.")
return
GLOBAL_CMD_TABLE.add_command("@cwho", cmd_cwho, help_category="Comms")
def cmd_ccreate(command):
"""
@ccreate
Usage:
@ccreate <new channel>
Creates a new channel owned by you.
"""
source_object = command.source_object
cname = command.command_argument
if not cname:
source_object.emit_to("Usage @ccreate <channelname>")
return
if not source_object.has_perm("objects.channel_admin"):
source_object.emit_to("Permission denied.")
return
name_matches = comsys.cname_search(cname, exact=True)
if name_matches:
source_object.emit_to("A channel with that name already exists.")
else:
# Create and set the object up.
new_chan = comsys.create_channel(cname, source_object)
source_object.emit_to("Channel %s created." % (new_chan.get_name(),))
GLOBAL_CMD_TABLE.add_command("@ccreate", cmd_ccreate, help_category="Comms")
def cmd_cchown(command):
"""
@cchown
Usage:
@cchown <channel> = <player>
Changes the owner of a channel.
"""
source_object = command.source_object
args = command.command_argument
if not args or "=" not in args:
source_object.emit_to("Usage: @cchown <channel> = <player>")
return
cname, pname = args.split("=",1)
cname, pname = cname.strip(), pname.strip()
#locate channel
try:
channel = CommChannel.objects.get(name__iexact=cname)
except CommChannel.DoesNotExist:
source_object.emit_to("Channel '%s' not found." % cname)
return
#check so we have ownership to give away.
if not channel.controlled_by(source_object) and not source_object.has_perm("channels.channel_admin"):
source_object.emit_to("You don't control this channel.")
return
#find the new owner
new_owner = Object.objects.player_name_search(pname)
if not new_owner:
source_object.emit_to("New owner '%s' not found." % pname)
return
old_owner = channel.get_owner()
old_pname = old_owner.get_name(show_dbref=False)
if old_owner == new_owner:
source_object.emit_to("Owner unchanged.")
return
#all is set, change owner
channel.set_owner(new_owner)
source_object.emit_to("Owner of %s changed from %s to %s." % (cname, old_pname, pname))
new_owner.emit_to("%s transfered ownership of channel '%s' to you." % (old_pname, cname))
GLOBAL_CMD_TABLE.add_command("@cchown", cmd_cchown, help_category="Comms")
def cmd_cdesc(command):
"""
@cdesc - set channel description
Usage:
@cdesc <channel> = <description>
Changes the description of the channel as shown in
channel lists.
"""
source_object = command.source_object
args = command.command_argument
if not args or "=" not in args:
source_object.emit_to("Usage: @cdesc <channel> = <description>")
return
cname, text = args.split("=",1)
cname, text = cname.strip(), text.strip()
#locate channel
try:
channel = CommChannel.objects.get(name__iexact=cname)
except CommChannel.DoesNotExist:
source_object.emit_to("Channel '%s' not found." % cname)
return
#check permissions
if not channel.controlled_by(source_object) \
and not source_object.has_perm("channels.channel_admin"):
source_object.emit_to("You don't control this channel.")
return
# set the description
channel.set_description(text)
source_object.emit_to("Description of channel '%s' set to '%s'." % (cname, text))
GLOBAL_CMD_TABLE.add_command("@cdesc", cmd_cdesc, help_category="Comms")

View file

@ -1,807 +0,0 @@
"""
Generic command module. Pretty much every command should go here for
now.
"""
import time
from django.contrib.auth.models import User
from src.objects.models import Object
from src.config.models import ConfigValue
from src.helpsys.models import HelpEntry
from src.ansi import ANSITable
from src import session_mgr
from src.util import functions_general
from src.helpsys import helpsystem
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_password(command):
"""
@password - set your password
Usage:
@password <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Usage: @password <oldpass> = <newpass>")
return
if not source_object.is_player():
source_object.emit_to("This is only applicable for players.")
return
eq_args = command.command_argument.split('=', 1)
if len(eq_args) != 2:
source_object.emit_to("Incorrect number of arguments.")
return
oldpass = eq_args[0]
newpass = eq_args[1]
if len(oldpass) == 0:
source_object.emit_to("You must provide your old password.")
elif len(newpass) == 0:
source_object.emit_to("You must provide your new password.")
else:
uaccount = source_object.get_user_account()
if not uaccount.check_password(oldpass):
source_object.emit_to("The specified old password isn't correct.")
elif len(newpass) < 3:
source_object.emit_to("Passwords must be at least three characters long.")
return
else:
uaccount.set_password(newpass)
uaccount.save()
source_object.emit_to("Password changed.")
GLOBAL_CMD_TABLE.add_command("@password", cmd_password, help_category="System")
def cmd_emit(command):
"""
@emit
Usage:
@emit[/switches] [<obj>, <obj>, ... =] <message>
Switches:
room : limit emits to rooms only
contents : send to the contents of objects
Emits a message to the selected objects or to
your immediate surroundings. If the object is a room,
send to its contents. @pemit and @remit are
restricted aliases to this main command.
"""
source_object = command.source_object
args = command.command_argument
switches = command.command_switches
if not args:
source_object.emit_to("Usage: @emit/switches [<obj>, <obj>, ... =] <message>")
return
if '=' in args:
args, message = [arg.strip() for arg in args.split('=',1)]
targets = [arg.strip() for arg in args.split(',')]
else:
targets = [source_object.get_location().dbref()]
message = args.strip()
# we now have a text to send and a list of target names.
# perform a global search for actual objects
tobjects = []
for target in targets:
if target in ['here']:
results = [source_object.get_location()]
elif target in ['me','my']:
results = [source_object]
else:
results = Object.objects.global_object_name_search(target)
if not results:
source_object.emit_to("No matches found for '%s'." % target)
return
if len(results) > 1:
string = "There are multiple matches. Please use #dbref to be more specific."
for result in results:
string += "\n %s" % results.get_name(show_dbref=True)
source_object.emit_to(string)
return
tobjects.append(results[0])
if not tobjects:
return
# sort the objects into categories
players = [obj for obj in tobjects if obj.is_player()]
rooms = [obj for obj in tobjects if obj.is_room()]
exits = [obj for obj in tobjects if obj.is_exit()]
things = [obj for obj in tobjects if obj.is_thing()]
# send differently depending on flags
if "room" in switches or "rooms" in switches:
# send to rooms only
norooms = players + exits + things
if norooms:
source_object.emit_to("These are not rooms: %s" %
", ".join([r.get_name() for r in norooms]))
return
for room in rooms:
room.emit_to_contents(message, exclude=source_object)
elif "contents" in switches:
# send to contents of objects
allobj = players + rooms + exits + things
for obj in allobj:
if not source_object.controls_other(obj):
source_object.emit_to("Cannot emit to %s (you don's control it)" % obj.get_name())
continue
obj.emit_to_contents(message)
else:
# assume reasonable defaults depending on object type
for obj in players:
obj.emit_to(message)
for obj in rooms:
obj.emit_to_contents(message)
for obj in exits: #send to destination
obj.get_home().emit_to_contents(message)
for obj in things:
if not source_object.controls_other(obj):
source_object.emit_to("Cannot emit to %s (you don's control it)" % obj.get_name())
continue
obj. emit_to_contents(message)
allobj = players + rooms + exits + things
string = ", ".join([obj.get_name() for obj in allobj])
source_object.emit_to("Emitted message to: %s." % string)
GLOBAL_CMD_TABLE.add_command("@emit", cmd_emit,
priv_tuple=("genperms.announce",),help_category="Comms")
def cmd_remit(command):
"""
@remit - emit to a room
Usage:
@remit <room> [<room2>,<room3>,...] = <message>
Emits message to the contents of the room.
"""
if not command.command_argument:
command.source_object.emit_to("Usage: @remit <room>[,<room2>,<room3>,...] = <message>")
return
command.command_switches = ["room"]
cmd_emit(command)
GLOBAL_CMD_TABLE.add_command("@remit", cmd_remit,
priv_tuple=("genperms.announce",),help_category="Comms")
def cmd_pemit(command):
"""
@pemit - emit to an object or player
Usage:
@pemit[/switch] <obj> [,<obj2>, <obj3>, ...] = <message>
Switches:
contents : emit to the contents of each object (only if you own it)
Emits message to objects or the contents of objects.
"""
if not command.command_argument:
command.source_object.emit_to("Usage: @pemit <obj>[,<obj2>,<obj3>,...] = <message>")
return
command.command_switches = [switch for switch in command.command_switches
if switch == 'contents']
cmd_emit(command)
GLOBAL_CMD_TABLE.add_command("@pemit", cmd_pemit,
priv_tuple=("genperms.announce",),help_category="Comms")
def cmd_wall(command):
"""
@wall
Usage:
@wall <message>
Announces a message to all connected players.
"""
wallstring = command.command_argument
if not wallstring:
command.source_object.emit_to("Announce what?")
return
message = "%s shouts \"%s\"" % (
command.source_object.get_name(show_dbref=False), wallstring)
session_mgr.announce_all(message)
GLOBAL_CMD_TABLE.add_command("@wall", cmd_wall,
priv_tuple=("genperms.announce",),help_category="Comms")
def cmd_idle(command):
"""
idle
Usage:
idle
Returns and does nothing. You can use this to send idle
messages to the game, in order to avoid getting timed out.
"""
pass
GLOBAL_CMD_TABLE.add_command("idle", cmd_idle, help_category="System")
def cmd_inventory(command):
"""
inventory
Usage:
inventory
inv
Shows a player's inventory.
"""
source_object = command.source_object
source_object.emit_to("You are carrying:")
for item in source_object.get_contents():
source_object.emit_to(" %s" % (item.get_name(),))
money = int(source_object.get_attribute_value("MONEY", default=0))
if money == 1:
money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_SINGULAR")
else:
money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_PLURAL")
source_object.emit_to("You have %d %s." % (money, money_name))
GLOBAL_CMD_TABLE.add_command("inventory", cmd_inventory)
def cmd_look(command):
"""
look
Usage:
look
look <obj>
Observers your location or objects in your vicinity.
"""
source_object = command.source_object
# If an argument is provided with the command, search for the object.
# else look at the current room.
if command.command_argument:
target_obj = source_object.search_for_object(command.command_argument)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
else:
target_obj = source_object.get_location()
# SCRIPT: Get the item's appearance from the scriptlink.
source_object.emit_to(target_obj.scriptlink.return_appearance(pobject=source_object))
# SCRIPT: Call the object's script's at_desc() method.
target_obj.scriptlink.at_desc(pobject=source_object)
GLOBAL_CMD_TABLE.add_command("look", cmd_look)
def cmd_get(command):
"""
get
Usage:
get <obj>
Picks up an object from your location and puts it in
your inventory.
"""
source_object = command.source_object
obj_is_staff = source_object.is_staff()
if not command.command_argument:
source_object.emit_to("Get what?")
return
else:
target_obj = source_object.search_for_object(command.command_argument,
search_contents=False)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
if source_object == target_obj:
source_object.emit_to("You can't get yourself.")
return
if not obj_is_staff and (target_obj.is_player() or target_obj.is_exit()):
source_object.emit_to("You can't get that.")
return
if target_obj.is_room() or target_obj.is_garbage() or target_obj.is_going():
source_object.emit_to("You can't get that.")
return
if not target_obj.scriptlink.default_lock(source_object):
lock_msg = target_obj.get_attribute_value("lock_msg")
if lock_msg:
source_object.emit_to(lock_msg)
else:
source_object.emit_to("You can't get that.")
return
target_obj.move_to(source_object, quiet=True)
source_object.emit_to("You pick up %s." % (target_obj.get_name(show_dbref=False),))
source_object.get_location().emit_to_contents("%s picks up %s." %
(source_object.get_name(show_dbref=False),
target_obj.get_name(show_dbref=False)),
exclude=source_object)
# SCRIPT: Call the object's script's a_get() method.
target_obj.scriptlink.at_get(source_object)
GLOBAL_CMD_TABLE.add_command("get", cmd_get)
def cmd_drop(command):
"""
drop
Usage:
drop <obj>
Has you drop an object from your inventory into the
location you are currently in.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Drop what?")
return
else:
target_obj = source_object.search_for_object(command.command_argument,
search_location=False)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
if not source_object == target_obj.get_location():
source_object.emit_to("You don't appear to be carrying that.")
return
target_obj.move_to(source_object.get_location(), quiet=True)
source_object.emit_to("You drop %s." % (target_obj.get_name(show_dbref=False),))
source_object.get_location().emit_to_contents("%s drops %s." %
(source_object.get_name(show_dbref=False),
target_obj.get_name(show_dbref=False)),
exclude=source_object)
# SCRIPT: Call the object script's a_drop() method.
target_obj.scriptlink.at_drop(source_object)
GLOBAL_CMD_TABLE.add_command("drop", cmd_drop),
def cmd_quit(command):
"""
quit
Usage:
quit
Gracefully disconnect from the game.
"""
if command.session:
session = command.session
session.msg("Quitting. Hope to see you soon again.")
session.handle_close()
GLOBAL_CMD_TABLE.add_command("quit", cmd_quit, help_category="System")
def cmd_who(command):
"""
who
Usage:
who
Shows who is currently online.
"""
session_list = session_mgr.get_session_list()
source_object = command.source_object
# In the case of the DOING command, don't show session data regardless.
if command.extra_vars and \
command.extra_vars.get("show_session_data", None) == False:
show_session_data = False
else:
show_session_data = source_object.has_perm("genperms.see_session_data")
# Only those with the see_session_data or superuser status can see
# session details.
if show_session_data:
retval = "Player Name On For Idle Room Cmds Host\n\r"
else:
retval = "Player Name On For Idle\n\r"
for player in session_list:
if not player.logged_in:
continue
delta_cmd = time.time() - player.cmd_last_visible
delta_conn = time.time() - player.conn_time
plr_pobject = player.get_pobject()
if show_session_data:
retval += '%-31s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n' % \
(plr_pobject.get_name(show_dbref=True, show_flags=False)[:25], \
# On-time
functions_general.time_format(delta_conn,0), \
# Idle time
functions_general.time_format(delta_cmd,1), \
# Flags
'', \
# Location
plr_pobject.get_location().id, \
player.cmd_total, \
# More flags?
'', \
player.address[0])
else:
retval += '%-31s%9s %4s%-3s\r\n' % \
(plr_pobject.get_name(show_dbref=False)[:25], \
# On-time
functions_general.time_format(delta_conn,0), \
# Idle time
functions_general.time_format(delta_cmd,1), \
# Flags
'')
retval += '%d Players logged in.' % (len(session_list),)
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("doing", cmd_who,
extra_vals={"show_session_data": False}, help_category="System")
GLOBAL_CMD_TABLE.add_command("who", cmd_who,help_category="System")
def cmd_say(command):
"""
say
Usage:
say <message>
Talk to those in your current location.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Say what?")
return
speech = command.command_argument
# Feedback for the object doing the talking.
source_object.emit_to("You say, '%s%s'" % (speech,
ANSITable.ansi['normal']))
# Build the string to emit to neighbors.
emit_string = "%s says, '%s'" % (source_object.get_name(show_dbref=False),
speech)
source_object.get_location().emit_to_contents(emit_string,
exclude=source_object)
GLOBAL_CMD_TABLE.add_command("say", cmd_say)
def cmd_fsay(command):
"""
@fsay - make an object say something
Usage:
@fsay <obj> = <text to say>
Make an object talk to its current location.
"""
source_object = command.source_object
args = command.command_argument
if not args or not "=" in args:
source_object.emit_to("Usage: @fsay <obj> = <text to say>")
return
target, speech = [arg.strip() for arg in args.split("=",1)]
# find object
if target in ['here']:
results = [source_object.get_location()]
elif target in ['me','my']:
results = [source_object]
else:
results = Object.objects.global_object_name_search(target)
if not results:
source_object.emit_to("No matches found for '%s'." % target)
return
if len(results) > 1:
string = "There are multiple matches. Please use #dbref to be more specific."
for result in results:
string += "\n %s" % results.get_name(show_dbref=True)
source_object.emit_to(string)
return
target = results[0]
# permission check
if not source_object.controls_other(target):
source_object.emit_to("Cannot pose %s (you don's control it)" % target.get_name())
return
# Feedback for the object doing the talking.
source_object.emit_to("%s says, '%s%s'" % (target.get_name(show_dbref=False),
speech,
ANSITable.ansi['normal']))
# Build the string to emit to neighbors.
emit_string = "%s says, '%s'" % (target.get_name(show_dbref=False),
speech)
target.get_location().emit_to_contents(emit_string,
exclude=source_object)
GLOBAL_CMD_TABLE.add_command("@fsay", cmd_fsay)
def cmd_pose(command):
"""
pose - strike a pose
Usage:
pose[/switches] <pose text>
Switches:
/nospace : put no space between your name
and the start of the pose.
Example:
pose is standing by the wall, smiling.
-> others will see:
Tom is standing by the wall, smiling.
Describe an action being taken. The pose text will
automatically begin with your name.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Do what?")
return
pose_string = command.command_argument
if "nospace" in command.command_switches:
# Output without a space between the player name and the emote.
sent_msg = "%s%s" % (source_object.get_name(show_dbref=False),
pose_string)
else:
# No switches, default.
sent_msg = "%s %s" % (source_object.get_name(show_dbref=False),
pose_string)
source_object.get_location().emit_to_contents(sent_msg)
GLOBAL_CMD_TABLE.add_command("pose", cmd_pose)
def cmd_fpose(command):
"""
@fpose - force an object to pose
Usage:
@fpose[/switches] <obj> = <pose text>
Switches:
nospace : put no text between the object's name
and the start of the pose.
Describe an action being taken as performed by obj.
The pose text will automatically begin with the name
of the object.
"""
source_object = command.source_object
args = command.command_argument
if not args or not "=" in args:
source_object.emit_to("Usage: @fpose <obj> = <pose text>")
return
target, pose_string = [arg.strip() for arg in args.split("=",1)]
# find object
if target in ['here']:
results = [source_object.get_location()]
elif target in ['me','my']:
results = [source_object]
else:
results = Object.objects.global_object_name_search(target)
if not results:
source_object.emit_to("No matches found for '%s'." % target)
return
if len(results) > 1:
string = "There are multiple matches. Please use #dbref to be more specific."
for result in results:
string += "\n %s" % results.get_name(show_dbref=True)
source_object.emit_to(string)
return
target = results[0]
# permission check
if not source_object.controls_other(target):
source_object.emit_to("Cannot pose %s (you don's control it)" % target.get_name())
return
if "nospace" in command.command_switches:
# Output without a space between the player name and the emote.
sent_msg = "%s%s" % (target.get_name(show_dbref=False),
pose_string)
else:
# No switches, default.
sent_msg = "%s %s" % (target.get_name(show_dbref=False),
pose_string)
source_object.get_location().emit_to_contents(sent_msg)
GLOBAL_CMD_TABLE.add_command("@fpose", cmd_fpose)
def cmd_group(command):
"""
@group - show your groups
Usage:
@group
This command shows you which user permission groups
you are a member of, if any.
"""
source_object = command.source_object
user = User.objects.get(username=source_object.get_name(show_dbref=False, no_ansi=True))
string = ""
if source_object.is_superuser():
string += "\n This is a SUPERUSER account! Group membership does not matter."
if not user.is_active:
string += "\n ACCOUNT NOT ACTIVE."
for group in user.groups.all():
string += "\n -- %s" % group
for perm in group.permissions.all():
string += "\n --- %s" % perm.name
if not string:
string = "You are not a member of any groups." % source_object.get_name(show_dbref=False)
else:
string = "\nYour (%s's) group memberships: %s" % (source_object.get_name(show_dbref=False), string)
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@group", cmd_group)
GLOBAL_CMD_TABLE.add_command("@groups", cmd_group, help_category="System")
def cmd_help(command):
"""
help - view help database
Usage:
help[/switches] <topic>
Switch:
apropos - show a list of all topics loosely matching the search criterion
(you can also use the commands 'apropos' or 'suggest' for this).
Examples: help index
help topic
help 345
help/apropos del
Shows the available help on <topic>. Use without <topic> to get the help
index. If more than one topic match your query, you will get a
list of topics to choose between. You can also supply a help entry number
directly if you know it.
"""
source_object = command.source_object
topicstr = command.command_argument
switches = command.command_switches
if not command.command_argument:
#display topic index if just help command is given
topicstr = "index"
if len(topicstr) < 2 and not topicstr.isdigit():
#check valid query
source_object.emit_to("Your search query must be at least two letters long.")
return
# speciel help index names. These entries are dynamically
# created upon request.
if topicstr in ['topic','topics']:
# the full index, affected by permissions
text = helpsystem.viewhelp.index_full(source_object)
text = " \nHELP TOPICS (By Category):\n\r%s" % text
source_object.emit_to(text)
return
elif 'index' in topicstr:
# view the category index
text = helpsystem.viewhelp.index_categories()
text = " \nHELP CATEGORIES (try 'help <category>' or 'help topics'):\n\r\n\r%s" % text
source_object.emit_to(text)
return
if switches and 'apropos' in switches:
# run a loose apropos match
topics = HelpEntry.objects.find_apropos(source_object, topicstr)
if topics:
if len(topics) > 50:
string = "Topics containing string '%s' (first 50):\n" % topicstr
topics = topics[:49]
else:
string = "Topics containing string '%s':\n" % topicstr
string += ", ".join(topic.get_topicname() for topic in topics)
else:
string = "No matches found for %s." % topicstr
source_object.emit_to(string)
return
# not a special help index entry. Do a search for the help entry.
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
# display help entry or handle no/multiple matches
string = ""
if not topics:
# no matches.
# try to see if it is matching the name of a category. If so,
# show the topics for this category.
text = helpsystem.viewhelp.index_category(source_object, topicstr)
if text:
# We have category matches, display the index and exit.
string = "\n%s%s%s\n\r\n\r%s" % ("---", " Help topics in category %s: " % \
topicstr.capitalize(), "-"* (30-len(topicstr)), text)
source_object.emit_to(string)
return
# at this point we just give a not-found error and give suggestions.
topics = HelpEntry.objects.find_topicsuggestions(source_object,
topicstr)
if topics:
if len(topics) > 3:
topics = topics[:3]
string += "\n\rMatching similarly named topics (use name or number to refine search):"
for entry in topics:
string += "\n %i.%s" % (entry.id, entry.topicname)
else:
string += "No matching topics found, please refine your search."
elif len(topics) > 1:
# multiple matches found
string += "More than one match found:"
for result in topics:
string += " %3d. %s" % (result.id, result.get_topicname())
else:
# a single match found
topic = topics[0]
header = "--- Help entry for '%s' (%s category) " % (topic.get_topicname(),
topic.get_category())
header = "%s%s" % (header, "-" * (80-len(header)))
string += "\n\r%s\n\r\n\r%s" % (header, topic.get_entrytext_ingame())
# add the 'See also:' footer
topics = HelpEntry.objects.find_topicsuggestions(source_object,
topic.get_topicname())
if topics:
if len(topics) > 5:
topics = topics[:5]
topics = [str(topic.topicname) for topic in topics ]
string += "\n\r\n\r" + " " * helpsystem.viewhelp.indent + \
"See also: " + ", ".join(topics)
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("help", cmd_help)
def cmd_apropos(command):
"""
apropos - show rough help matches
Usage:
apropos <text>
or
suggest <text>
This presents a list of topics very loosely matching your
search text. Use this command when you are searching for
help on a certain concept but don't know any exact
command names. You can also use the normal help command
with the /apropos switch to get the same functionality.
"""
arg = command.command_argument
command.source_object.execute_cmd("help/apropos %s" % arg)
GLOBAL_CMD_TABLE.add_command("apropos", cmd_apropos)
GLOBAL_CMD_TABLE.add_command("suggest", cmd_apropos)

View file

@ -1,211 +0,0 @@
"""
IMC2 user and administrative commands.
"""
from django.conf import settings
from src import comsys
from src.cmdtable import GLOBAL_CMD_TABLE
from src.ansi import parse_ansi
from src.imc2.imc_ansi import IMCANSIParser
from src.imc2 import connection as imc2_conn
from src.imc2.packets import *
from src.imc2.models import IMC2ChannelMapping
from src.imc2.trackers import IMC2_MUDLIST, IMC2_CHANLIST
from src.channels.models import CommChannel
def cmd_imcwhois(command):
"""
imcwhois
Usage:
imcwhois
IMC2 command. Shows a player's inventory.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Get what?")
return
else:
source_object.emit_to("Sending IMC whois request. If you receive no response, no matches were found.")
packet = IMC2PacketWhois(source_object, command.command_argument)
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet)
GLOBAL_CMD_TABLE.add_command("imcwhois", cmd_imcwhois, help_category="Comms")
def cmd_imcansi(command):
"""
imcansi
Usage:
imcansi <string>
Test IMC ANSI conversion.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("You must provide a string to convert.")
return
else:
retval = parse_ansi(command.command_argument, parser=IMCANSIParser())
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcansi", cmd_imcansi, help_category="Comms")
def cmd_imcicerefresh(command):
"""
imcicerefresh
Usage:
imcicerefresh
IMC2: Semds an ice-refresh packet.
"""
source_object = command.source_object
packet = IMC2PacketIceRefresh()
imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet)
source_object.emit_to("Sent")
GLOBAL_CMD_TABLE.add_command("imcicerefresh", cmd_imcicerefresh, help_category="Comms")
def cmd_imcchanlist(command):
"""
imcchanlist
Usage:
imcchanlist
Shows the list of cached channels from the IMC2 Channel list.
"""
source_object = command.source_object
retval = 'Channels on %s\n\r' % imc2_conn.IMC2_PROTOCOL_INSTANCE.network_name
retval += ' Full Name Name Owner Perm Policy\n\r'
retval += ' --------- ---- ----- ---- ------\n\r'
for channel in IMC2_CHANLIST.get_channel_list():
retval += ' %-18s %-10s %-15s %-7s %s\n\r' % (channel.name,
channel.localname,
channel.owner,
channel.level,
channel.policy)
retval += '%s channels found.' % len(IMC2_CHANLIST.chan_list)
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcchanlist", cmd_imcchanlist, help_category="Comms")
def cmd_imclist(command):
"""
imclist
Usage:
imclist
Shows the list of cached games from the IMC2 Mud list.
"""
source_object = command.source_object
retval = 'Active MUDs on %s\n\r' % imc2_conn.IMC2_PROTOCOL_INSTANCE.network_name
for mudinfo in IMC2_MUDLIST.get_mud_list():
mudline = ' %-20s %s' % (mudinfo.name, mudinfo.versionid)
retval += '%s\n\r' % mudline[:78]
retval += '%s active MUDs found.' % len(IMC2_MUDLIST.mud_list)
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imclist", cmd_imclist, help_category="Comms")
def cmd_imcstatus(command):
"""
imcstatus
Usage:
imcstatus
Shows some status information for your IMC2 connection.
"""
source_object = command.source_object
# This manages our game's plugged in services.
collection = command.session.server.service_collection
# Retrieve the IMC2 service.
service = collection.getServiceNamed('IMC2')
if service.running == 1:
status_string = 'Running'
else:
status_string = 'Inactive'
# Build the output to emit to the player.
retval = '-' * 50
retval += '\n\r'
retval += 'IMC Status\n\r'
retval += ' * MUD Name: %s\n\r' % (settings.IMC2_MUDNAME)
retval += ' * Status: %s\n\r' % (status_string)
retval += ' * Debugging Mode: %s\n\r' % (settings.IMC2_DEBUG)
retval += ' * IMC Network Address: %s\n\r' % (settings.IMC2_SERVER_ADDRESS)
retval += ' * IMC Network Port: %s\n\r' % (settings.IMC2_SERVER_PORT)
retval += '-' * 50
source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("imcstatus", cmd_imcstatus,
priv_tuple=('imc2.admin_imc_channels',), help_category="Comms")
def cmd_IMC2chan(command):
"""
@imc2chan
Usage:
@imc2chan <IMCServer> : <IMCchannel> <channel>
Links an IMC channel to an existing evennia
channel. You can link as many existing
evennia channels as you like to the
IMC channel this way. Running the command with an
existing mapping will re-map the channels.
Use 'imcchanlist' to get a list of IMC channels and
servers. Note that both are case sensitive.
"""
source_object = command.source_object
if not settings.IMC2_ENABLED:
s = """IMC is not enabled. You need to activate it in game/settings.py."""
source_object.emit_to(s)
return
args = command.command_argument
if not args or len(args.split()) != 2 :
source_object.emit_to("Usage: @imc2chan IMCServer:IMCchannel channel")
return
#identify the server-channel pair
imcdata, channel = args.split()
if not ":" in imcdata:
source_object.emit_to("You need to supply an IMC Server:Channel pair.")
return
imclist = IMC2_CHANLIST.get_channel_list()
imc_channels = filter(lambda c: c.name == imcdata, imclist)
if not imc_channels:
source_object.emit_to("IMC server and channel '%s' not found." % imcdata)
return
else:
imc_server_name, imc_channel_name = imcdata.split(":")
#find evennia channel
try:
chanobj = comsys.get_cobj_from_name(channel)
except CommChannel.DoesNotExist:
source_object.emit_to("Local channel '%s' not found (use real name, not alias)." % channel)
return
#create the mapping.
outstring = ""
mapping = IMC2ChannelMapping.objects.filter(channel__name=channel)
if mapping:
mapping = mapping[0]
outstring = "Replacing %s. New " % mapping
else:
mapping = IMC2ChannelMapping()
mapping.imc2_server_name = imc_server_name
mapping.imc2_channel_name = imc_channel_name
mapping.channel = chanobj
mapping.save()
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@imc2chan",cmd_IMC2chan,
priv_tuple=("imc2.admin_imc_channels",), help_category="Comms")

View file

@ -1,223 +0,0 @@
"""
Commands that are generally staff-oriented that show information regarding
the server instance.
"""
import os
import time
from src.util import functions_general
if not functions_general.host_os_is('nt'):
# Don't import the resource module if the host OS is Windows.
import resource
import django
from django.conf import settings
from src.objects.models import Object
from src import scheduler
from src import defines_global
from src import flags
from src.cmdtable import GLOBAL_CMD_TABLE
from src.cache import cache
from src import gametime
def cmd_version(command):
"""
@version - game version
Usage:
@version
Display the game version info
"""
retval = "-"*50 +"\n\r"
retval += " Evennia %s\n\r" % (defines_global.EVENNIA_VERSION,)
retval += " Django %s\n\r" % (django.get_version())
retval += "-"*50
command.source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("@version", cmd_version, help_category="Admin"),
def cmd_time(command):
"""
@time
Usage:
@time
Server local time.
"""
gtime = gametime.time()
gtime_h = functions_general.time_format(gtime, style=2)
ictime = gtime * settings.TIME_FACTOR
ictime_h = functions_general.time_format(ictime, style=2)
uptime = time.time() - command.session.server.start_time
uptime_h = functions_general.time_format(uptime, style=2)
synctime = gametime.time_last_sync()
synctime_h = functions_general.time_format(synctime, style=2)
ltime = time.strftime('%a %b %d %H:%M:%S %Y (%Z)', time.localtime())
string = " Real-world times:"
string += "\n -- Main time counter: %s (%i s)." % (gtime_h, gtime)
string += "\n -- Time since last reboot: %s (%i s). " % (uptime_h, uptime)
string += "\n -- Time since cache was last saved: %s (%i s)." % (synctime_h,
synctime)
string += "\n -- Current server time: %s" % ltime
string += "\n In-game time (time factor %s):" % settings.TIME_FACTOR
string += "\n -- Time passed: %s" % ictime_h
command.source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@time", cmd_time, priv_tuple=("genperms.game_info",),
help_category="Admin")
def cmd_uptime(command):
"""
@uptime
Usage:
@uptime
Server uptime and stats.
"""
source_object = command.source_object
server = command.session.server
start_delta = time.time() - server.start_time
string = " Server time info:"
string += "\n -- Current server time : %s" % \
(time.strftime('%a %b %d %H:%M %Y (%Z)', time.localtime(),))
string += "\n -- Server start time : %s" % \
(time.strftime('%a %b %d %H:%M %Y',
time.localtime(server.start_time),))
string += "\n -- Server uptime : %s" % \
(functions_general.time_format(start_delta, style=2))
if not functions_general.host_os_is('nt'):
# os.getloadavg() is not available on Windows.
loadavg = os.getloadavg()
string += "\n -- Server load (1 min) : %.2f" % loadavg[0]
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@uptime",
cmd_uptime,
priv_tuple=("genperms.game_info",),
help_category="Admin")
def cmd_list(command):
"""
@list - list info
Usage:
@list commands | flags | process
Shows game related information depending
on which argument is given.
"""
server = command.session.server
source_object = command.source_object
msg_invalid = "Usage @list commands|flags|process"
if not command.command_argument:
source_object.emit_to(msg_invalid)
elif command.command_argument == "commands":
clist = GLOBAL_CMD_TABLE.ctable.keys()
clist.sort()
source_object.emit_to('Commands: '+ ' '.join(clist))
elif command.command_argument == "process":
if not functions_general.host_os_is('nt'):
loadvg = os.getloadavg()
psize = resource.getpagesize()
rusage = resource.getrusage(resource.RUSAGE_SELF)
source_object.emit_to("Process ID: %10d %10d bytes per page" %
(os.getpid(), psize))
source_object.emit_to("Time used: %10d user %10d sys" %
(rusage[0],rusage[1]))
source_object.emit_to("Integral mem:%10d shared %10d private%10d stack" %
(rusage[3], rusage[4], rusage[5]))
source_object.emit_to("Max res mem: %10d pages %10d bytes" %
(rusage[2],rusage[2] * psize))
source_object.emit_to("Page faults: %10d hard %10d soft %10d swapouts" %
(rusage[7], rusage[6], rusage[8]))
source_object.emit_to("Disk I/O: %10d reads %10d writes" %
(rusage[9], rusage[10]))
source_object.emit_to("Network I/O: %10d in %10d out" %
(rusage[12], rusage[11]))
source_object.emit_to("Context swi: %10d vol %10d forced %10d sigs" %
(rusage[14], rusage[15], rusage[13]))
else:
source_object.emit_to("Feature not available on Windows.")
return
elif command.command_argument == "flags":
source_object.emit_to("Flags: "+" ".join(flags.SERVER_FLAGS))
else:
source_object.emit_to(msg_invalid)
GLOBAL_CMD_TABLE.add_command("@list", cmd_list,priv_tuple=("genperms.game_info",), help_category="Admin")
def cmd_ps(command):
"""
@ps - list processes
Usage
@ps
Shows the process/event table.
"""
source_object = command.source_object
source_object.emit_to("Processes Scheduled:\n-- PID [time/interval] [repeats] description --")
for event in scheduler.SCHEDULE:
repeats = "[inf] "
if event.repeats != None:
repeats = "[%i] " % event.repeats
source_object.emit_to(" %i [%d/%d] %s%s" % (
event.pid,
event.get_nextfire(),
event.interval,
repeats,
event.description))
source_object.emit_to("Totals: %d interval events" % (len(scheduler.SCHEDULE),))
GLOBAL_CMD_TABLE.add_command("@ps", cmd_ps,
priv_tuple=("genperms.process_control",), help_category="Admin")
def cmd_stats(command):
"""
@stats - show object stats
Usage:
@stats
Example:
@stats
->
4012 objects = 144 rooms, 212 exits, 613 things, 1878 players. (1165 garbage)
Shows stats about the database.
"""
stats_dict = Object.objects.object_totals()
command.source_object.emit_to(
"%d objects = %d rooms, %d exits, %d things, %d players. (%d garbage)" %
(stats_dict["objects"],
stats_dict["rooms"],
stats_dict["exits"],
stats_dict["things"],
stats_dict["players"],
stats_dict["garbage"]))
GLOBAL_CMD_TABLE.add_command("@stats", cmd_stats, priv_tuple=("genperms.game_info",), help_category="Admin"),
def cmd_showcache(command):
"""
@showcache - show stats about the cache system
Usage:
@showcache
Study the current contents and size of the cache.
"""
source_object = command.source_object
str_cache, str_pcache = cache.show()
ncache = len(str_cache.split(','))
npcache = len(str_pcache.split(','))
string = ""
if str_cache:
string += "\nVolatile cache (%i):\n %s" % (ncache, str_cache)
if str_pcache:
string += "\nPersistent cache (%i):\n %s" % (npcache, str_pcache)
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@showcache", cmd_showcache, priv_tuple=("genperms.game_info",), help_category="Admin"),

View file

@ -1,130 +0,0 @@
"""
IRC-related commands
"""
from twisted.application import internet
from django.conf import settings
from src.irc.connection import IRC_CHANNELS
from src.irc.models import IRCChannelMapping
from src import comsys
from src.cmdtable import GLOBAL_CMD_TABLE
from src.channels.models import CommChannel
def cmd_IRC2chan(command):
"""
@irc2chan - link irc to ingame channel
Usage:
@irc2chan <#IRCchannel> <local channel>
Links an IRC channel (including #) to an existing
evennia channel. You can link as many existing
evennia channels as you like to the
IRC channel this way. Running the command with an
existing mapping will re-map the channels.
"""
source_object = command.source_object
if not settings.IRC_ENABLED:
s = """IRC is not enabled. You need to activate it in game/settings.py."""
source_object.emit_to(s)
return
args = command.command_argument
if not args or len(args.split()) != 2 :
source_object.emit_to("Usage: @irc2chan IRCchannel channel")
return
irc_channel, channel = args.split()
if irc_channel not in [o.factory.channel for o in IRC_CHANNELS]:
source_object.emit_to("IRC channel '%s' not found." % irc_channel)
return
try:
chanobj = comsys.get_cobj_from_name(channel)
except CommChannel.DoesNotExist:
source_object.emit_to("Local channel '%s' not found (use real name, not alias)." % channel)
return
#create the mapping.
outstring = ""
mapping = IRCChannelMapping.objects.filter(channel__name=channel)
if mapping:
mapping = mapping[0]
outstring = "Replacing %s. New " % mapping
else:
mapping = IRCChannelMapping()
mapping.irc_server_name = settings.IRC_NETWORK
mapping.irc_channel_name = irc_channel
mapping.channel = chanobj
mapping.save()
outstring += "Mapping set: %s." % mapping
source_object.emit_to(outstring)
GLOBAL_CMD_TABLE.add_command("@irc2chan",cmd_IRC2chan,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCjoin(command):
"""
@ircjoin - join a new irc channel
Usage:
@ircjoin <#IRCchannel>
Attempts to connect a bot to a new IRC channel (don't forget that
IRC channels begin with a #).
The bot uses the connection details defined in the main settings.
Observe that channels added using this command does not survive a reboot.
"""
source_object = command.source_object
arg = command.command_argument
if not arg:
source_object.emit_to("Usage: @ircjoin #irc_channel")
return
channel = arg.strip()
if channel[0] != "#": channel = "#%s" % channel
if not settings.IRC_ENABLED:
source_object.emit_to("IRC services are not active. You need to turn them on in preferences.")
return
#direct creation of bot (do not add to services)
from src.irc.connection import connect_to_IRC
connect_to_IRC(settings.IRC_NETWORK,
settings.IRC_PORT,
channel, settings.IRC_NICKNAME)
# ---below should be checked so as to add subequent IRC bots to Services.
# it adds just fine, but the bot does not connect. /Griatch
# from src.irc.connection import IRC_BotFactory
# from src.server import mud_service
# irc = internet.TCPClient(settings.IRC_NETWORK,
# settings.IRC_PORT,
# IRC_BotFactory(channel,
# settings.IRC_NETWORK,
# settings.IRC_NICKNAME))
# irc.setName("%s:%s" % ("IRC",channel))
# irc.setServiceParent(mud_service.service_collection)
GLOBAL_CMD_TABLE.add_command("@ircjoin",cmd_IRCjoin,
priv_tuple=("irc.admin_irc_channels",),
help_category="Comms")
def cmd_IRCchanlist(command):
"""
ircchanlist
Usage:
ircchanlist
Lists all externally available IRC channels.
"""
source_object = command.source_object
s = "Available IRC channels:"
for c in IRC_CHANNELS:
s += "\n %s \t(nick '%s') on %s" % (c.factory.channel,
c.factory.nickname,
c.factory.network,)
source_object.emit_to(s)
GLOBAL_CMD_TABLE.add_command("ircchanlist", cmd_IRCchanlist,
help_category="Comms")

File diff suppressed because it is too large Load diff

View file

@ -1,136 +0,0 @@
"""
Paging command and support functions.
"""
from src.objects.models import Object
from src.cmdtable import GLOBAL_CMD_TABLE
def get_last_paged_objects(source_object):
"""
Returns a list of objects of the user's last paged list, or None if invalid
or non-existant.
"""
last_paged_dbrefs = source_object.get_attribute_value("LASTPAGED", None)
if last_paged_dbrefs:
last_paged_objects = list()
try:
last_paged_dbref_list = [
x.strip() for x in last_paged_dbrefs.split(',')
]
for dbref in last_paged_dbref_list:
if not Object.objects.is_dbref(dbref):
raise ValueError
last_paged_object = Object.objects.dbref_search(dbref)
if last_paged_object is not None:
last_paged_objects.append(last_paged_object)
return last_paged_objects
except ValueError:
# Remove the invalid LASTPAGED attribute
source_object.clear_attribute("LASTPAGED")
return None
def cmd_page(command):
"""
page - send private message
Usage:
page [<user> = <message>]
Send a message to target user (if online). If no
argument is given, you will instead see who was the last
person you paged to.
"""
source_object = command.source_object
# Get the last paged person(s)
last_paged_objects = get_last_paged_objects(source_object)
# If they don't give a target, or any data to send to the target
# then tell them who they last paged if they paged someone, if not
# tell them they haven't paged anyone.
if not command.command_argument:
if last_paged_objects:
source_object.emit_to("You last paged: %s." % (
', '.join([x.name for x in last_paged_objects])))
return
else:
# No valid LASTPAGE values
source_object.emit_to("You have not paged anyone.")
return
# Stores a list of targets
targets = []
# Build a list of targets
# If there are no targets, then set the targets to the last person they
# paged.
cmd_targets = command.get_arg_targets()
if cmd_targets is None:
targets = last_paged_objects
# No valid last paged players found, error out.
if not targets:
source_object.emit_to("Page who?")
return
else:
# For each of the targets listed, grab their objects and append
# it to the targets list if valid.
for target in cmd_targets:
matched_object = Object.objects.player_name_search(target)
if matched_object:
# Found a good object, store it
targets.append(matched_object)
else:
# Search returned None
source_object.emit_to("Player '%s' can not be found." % (
target))
# Depending on the argument provided, either send the entire thing as
# a message or break off the point after the equal sign.
if command.arg_has_target():
# User specified targets, get the stuff after the equal sign.
message = command.get_arg_target_value()
else:
# No targets specified with equal sign, use lastpaged and the user's
# arguments as the message to send.
message = command.command_argument
sender_name = source_object.get_name(show_dbref=False)
# Build our messages
target_message = "%s pages: %s"
sender_message = "You paged %s with '%s'."
# Handle paged emotes
if message.startswith(':'):
message = message[1:]
target_message = "From afar, %s %s"
sender_message = "Long distance to %s: %s %s"
# Handle paged emotes without spaces
elif message.startswith(';'):
message = message[1:]
target_message = "From afar, %s%s"
sender_message = "Long distance to %s: %s%s"
# We build a list of target_names for the sender_message later
target_names = []
for target in targets:
# Check to make sure they're connected, or a player
if target.is_connected_plr():
target.emit_to(target_message % (sender_name, message))
target_names.append(target.get_name(show_dbref=False))
else:
source_object.emit_to("Player %s does not exist or is not online." % (
target.get_name(show_dbref=False)))
# Now send a confirmation to the person doing the paging.
if len(target_names) > 0:
target_names_string = ', '.join(target_names)
try:
source_object.emit_to(sender_message % (target_names_string,
sender_name, message))
except TypeError:
source_object.emit_to(sender_message % (target_names_string,
message))
# Now set the LASTPAGED attribute
source_object.set_attribute("LASTPAGED", ','.join(
["#%d" % (x.id) for x in targets]))
GLOBAL_CMD_TABLE.add_command("page", cmd_page, priv_tuple=('channels.page',), help_category="Comms")

View file

@ -1,97 +0,0 @@
"""
Contains commands for managing script parents.
"""
from src import scripthandler
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE
def cmd_scriptcache(command):
"""
@scriptcache
Usage
@scriptcache
Shows the contents of the script cache.
"""
cache_dict = scripthandler.CACHED_SCRIPTS
retval = "Currently Cached Script Parents\n"
retval += "-" * 78
for script in cache_dict.keys():
retval += "\n " + script
retval += "\n" + "-" * 78 + "\n"
retval += "%d cached parents" % len(cache_dict)
command.source_object.emit_to(retval)
GLOBAL_CMD_TABLE.add_command("@scriptcache", cmd_scriptcache,
priv_tuple=("genperms.builder",), help_category="Admin")
def cmd_parent(command):
"""
@parent - set script parent
Usage:
@parent <object> = <parent>
Example:
@parent button = examples.red_button
Sets an object's script parent. The parent must be identified
by its location using dot-notation pointing to the script
parent module.
"""
source_object = command.source_object
if not command.command_argument:
source_object.emit_to("Usage: @parent <object> [=<parent]")
return
eq_args = command.command_argument.split('=', 1)
target_name = eq_args[0]
target_obj = source_object.search_for_object(target_name)
if not target_obj:
return
if len(eq_args) > 1:
#if we gave a command of the form @parent obj=something we want to
#somehow affect the parent.
parent_name = eq_args[1]
#check permissions
if not source_object.controls_other(target_obj):
source_object.emit_to(defines_global.NOCONTROL_MSG)
return
# Clear parent if command was @parent obj= or obj=none
if not parent_name or parent_name.lower() == "none":
target_obj.set_script_parent(None)
target_obj.scriptlink.at_object_creation()
new_parent = target_obj.scriptlink()
source_object.emit_to("%s reverted to its default parent (%s)." %
(target_obj, new_parent))
return
# If we reach this point, attempt to change parent.
former_parent = target_obj.get_scriptlink()
if target_obj.set_script_parent(parent_name):
#new script path added; initialize the parent
target_obj.scriptlink.at_object_creation()
s = "%s's parent is now %s (instead of %s).\n\r"
s += "Note that the new parent type could have overwritten "
s += "same-named attributes on the existing object."
source_object.emit_to(s)
else:
source_object.emit_to("'%s' is not a valid parent path." % parent_name)
else:
# We haven't provided a target; list the current parent
current_parent = target_obj.get_scriptlink()
source_object.emit_to("Current parent of %s is %s." %
(target_obj,current_parent))
GLOBAL_CMD_TABLE.add_command("@parent", cmd_parent,
priv_tuple=("genperms.builder",), help_category="Building" )

View file

@ -1,879 +0,0 @@
"""
This file contains commands that require special permissions to use. These
are generally @-prefixed commands, but there are exceptions.
"""
from django.contrib.auth.models import Permission, Group, User
from django.conf import settings
from src.objects.models import Object
from src import session_mgr
from src import comsys
from src.scripthandler import rebuild_cache
from src.cmdtable import GLOBAL_CMD_TABLE
from src.helpsys.models import HelpEntry
from src.helpsys import helpsystem
from src.config.models import CommandAlias
from src.config import edit_aliases
from src import cache
from src import scheduler
def cmd_reload(command):
"""
@reload - reload game subsystems
Usage:
@reload/switches
Switches:
aliases - alias definitions
commands - the command modules
scripts, parents - the script parent modules
all - reload all of the above
cache - flush the volatile cache (warning, this
might cause unexpected results if your
script parents use the cache a lot)
reset - flush cache then reload all
Reloads all the identified subsystems. Flushing the cache is
generally not needed outside the main game development.
"""
source_object = command.source_object
switches = command.command_switches
if not switches or switches[0] not in ['all','aliases','alias',
'commands','command',
'scripts','parents',
'cache','reset']:
source_object.emit_to("Usage: @reload/<aliases|scripts|commands|all>")
return
switch = switches[0]
sname = source_object.get_name(show_dbref=False)
if switch in ["reset", "cache"]:
# Clear the volatile cache
cache.flush()
comsys.cemit_mudinfo("%s flushed the non-persistent cache." % sname)
if switch in ["reset","all","aliases","alias"]:
# Reload Aliases
command.session.server.reload_aliases(source_object=source_object)
comsys.cemit_mudinfo("%s reloaded Aliases." % sname)
if switch in ["reset","all","scripts","parents"]:
# Reload Script parents
rebuild_cache()
comsys.cemit_mudinfo("%s reloaded Script parents." % sname)
if switch in ["reset","all","commands","command"]:
# Reload command objects.
comsys.cemit_mudinfo("%s is reloading Command modules ..." % sname)
command.session.server.reload(source_object=command.source_object)
comsys.cemit_mudinfo("... all Command modules were reloaded.")
GLOBAL_CMD_TABLE.add_command("@reload", cmd_reload,
priv_tuple=("genperms.process_control",), help_category="Admin")
GLOBAL_CMD_TABLE.add_command("@restart", cmd_reload,
priv_tuple=("genperms.process_control",), help_category="Admin")
def cmd_boot(command):
"""
@boot
Usage
@boot[/switches] <player obj> [: reason]
Switches:
quiet - Silently boot without informing player
port - boot by port number instead of name or dbref
Boot a player object from the server. If a reason is
supplied it will be echoed to the user unless /quiet is set.
"""
source_object = command.source_object
switch_quiet = False
switch_port = False
if "quiet" in command.command_switches:
# Don't tell the player they've been disconnected, silently boot them.
switch_quiet = True
if "port" in command.command_switches:
# Boot by port number instead of name or dbref.
switch_port = True
if not command.command_argument:
source_object.emit_to("Usage: @boot[/switches] <player> [:reason]")
return
else:
arg = command.command_argument
reason = ""
if ':' in arg:
arg, reason = [a.strip() for a in arg.split(':',1)]
boot_list = []
if switch_port:
# Boot a particular port.
sessions = session_mgr.get_session_list(True)
for sess in sessions:
# Find the session with the matching port number.
if sess.getClientAddress()[1] == int(arg):
boot_list.append(sess)
# Match found, kill the loop and continue with booting.
break
else:
# Grab the objects that match
objs = Object.objects.local_and_global_search(source_object, arg)
if not objs:
source_object.emit_to("No name or dbref match found for booting.")
return
if not objs[0].is_player():
source_object.emit_to("You can only boot players.")
return
if not source_object.controls_other(objs[0]):
if objs[0].is_superuser():
source_object.emit_to("You cannot boot a Wizard.")
return
else:
source_object.emit_to("You do not have permission to boot that player.")
return
if objs[0].is_connected_plr():
matches = session_mgr.sessions_from_object(objs[0])
for match in matches:
boot_list.append(match)
else:
source_object.emit_to("That player is not connected.")
return
if not boot_list:
source_object.emit_to("No matches found.")
return
# Carry out the booting of the sessions in the boot list.
for boot in boot_list:
if not switch_quiet:
msg = "You have been disconnected by %s." % (source_object.name)
if reason:
msg += "\n Reason given:\n '%s'" % reason
boot.msg(msg)
boot.disconnectClient()
session_mgr.remove_session(boot)
return
GLOBAL_CMD_TABLE.add_command("@boot", cmd_boot,
priv_tuple=("genperms.manage_players",),
help_category="Admin")
def cmd_delplayer(command):
"""
delplayer - delete player from server
Usage:
@delplayer <name> [: reason]
Completely deletes a user from the server database,
making their nick and e-mail again available.
"""
source_object = command.source_object
arg = command.command_argument
if not arg:
source_object.emit_to("Usage: @delplayer <player name or #id>")
return
reason = ""
if ':' in arg:
arg, reason = [a.strip() for a in arg.split(':',1)]
objs = Object.objects.local_and_global_search(source_object, arg)
if not objs:
source_object.emit_to("No player object matches found for '%s'." % arg)
return
pobj = objs[0]
if not source_object.controls_other(pobj):
if pobj.is_superuser():
source_object.emit_to("You cannot delete a Superuser.")
return
else:
source_object.emit_to("You do not have permission to delete that player.")
return
# boot the player then delete
source_object.emit_to("Booting and informing player if currently online ...")
name = pobj.get_name()
msg = "\nYour account '%s' is being *permanently* deleted.\n" % name
if reason:
msg += " Reason given:\n '%s'" % reason
pobj.emit_to(msg)
source_object.execute_cmd("@boot %s" % arg)
pobj.delete()
source_object.emit_to("Player %s was successfully deleted." % name)
GLOBAL_CMD_TABLE.add_command("@delplayer", cmd_delplayer,
priv_tuple=("genperms.manage_players",),
help_category="Admin")
def cmd_newpassword(command):
"""
@newpassword
Usage:
@newpassword <user obj> = <new password>
Set a player's password.
"""
source_object = command.source_object
eq_args = command.command_argument.split('=', 1)
searchstring = eq_args[0]
newpass = eq_args[1]
if not command.command_argument or len(searchstring) == 0:
source_object.emit_to("What player's password do you want to change?")
return
if len(newpass) == 0:
source_object.emit_to("You must supply a new password.")
return
target_obj = source_object.search_for_object(searchstring)
# Use search_for_object to handle duplicate/nonexistant results.
if not target_obj:
return
if not target_obj.is_player():
source_object.emit_to("You can only change passwords on players.")
elif not source_object.controls_other(target_obj):
source_object.emit_to("You do not control %s." % (target_obj.get_name(),))
else:
uaccount = target_obj.get_user_account()
if len(newpass) == 0:
uaccount.set_password()
else:
uaccount.set_password(newpass)
uaccount.save()
source_object.emit_to("%s - PASSWORD set." % (target_obj.get_name(),))
target_obj.emit_to("%s has changed your password." %
(source_object.get_name(show_dbref=False),))
GLOBAL_CMD_TABLE.add_command("@newpassword", cmd_newpassword,
priv_tuple=("genperms.manage_players",),
help_category="Admin")
def cmd_home(command):
"""
home
Usage:
home
Teleport the player to their home.
"""
pobject = command.source_object
if pobject.home == None:
pobject.emit_to("You have no home set, @link yourself to somewhere.")
else:
pobject.emit_to("There's no place like home...")
pobject.move_to(pobject.get_home())
GLOBAL_CMD_TABLE.add_command("home", cmd_home,
priv_tuple=("genperms.tel_anywhere",))
def cmd_service(command):
"""
@service - manage services
Usage:
@service[/switch] <service>
Switches:
start - activates a service
stop - stops a service
list - shows all available services
Service management system. Allows for the listing,
starting, and stopping of services.
"""
source_object = command.source_object
switches = command.command_switches
if not switches or switches[0] not in ["list","start","stop"]:
source_object.emit_to("Usage: @servive/<start|stop|list> [service]")
return
switch = switches[0].strip()
sname = source_object.get_name(show_dbref=False)
if switch == "list":
#Just display the list of installed services and their status, then exit.
s = "-" * 40
s += "\nService Listing"
for service in command.session.server.service_collection.services:
# running is either 1 or 0, 1 meaning the service is running.
if service.running == 1:
status = 'Running'
else:
status = 'Inactive'
s += '\n * %s (%s)' % (service.name, status)
s += "\n" + "-" * 40
source_object.emit_to(s)
return
if switch in ["stop", "start"]:
# This stuff is common to both start and stop switches.
collection = command.session.server.service_collection
try:
service = collection.getServiceNamed(command.command_argument)
except:
source_object.emit_to('Invalid service name. This command is case-sensitive. See @service/list.')
return
if switch == "stop":
"""
Stopping a service gracefully closes it and disconnects any connections
(if applicable).
"""
if service.running == 0:
source_object.emit_to('That service is not currently running.')
return
# We don't want killing main Evennia TCPServer services here. If
# wanting to kill a listening port, one needs to do it through
# settings.py and a restart.
if service.name[:7] == 'Evennia':
s = "You can not stop Evennia TCPServer services this way."
s += "\nTo e.g. remove a listening port, change settings file and restart."
source_object.emit_to(s)
return
comsys.cemit_mudinfo("%s is *Stopping* the service '%s'." % (sname, service.name))
service.stopService()
return
if switch == "start":
"""
Starts a service.
"""
if service.running == 1:
source_object.emit_to('That service is already running.')
return
comsys.cemit_mudinfo("%s is *Starting* the service '%s'." % (sname,service.name))
service.startService()
return
GLOBAL_CMD_TABLE.add_command("@service", cmd_service,
priv_tuple=("genperms.process_control",),
help_category="Admin")
def cmd_shutdown(command):
"""
@shutdown
Usage:
@shutdown
Shut the game server down gracefully.
"""
command.source_object.emit_to('Shutting down...')
print 'Server shutdown by %s' % (command.source_object.get_name(show_dbref=False),)
command.session.server.shutdown()
GLOBAL_CMD_TABLE.add_command("@shutdown", cmd_shutdown,
priv_tuple=("genperms.process_control",),
help_category="Admin")
# permission administration
# Django automatically creates a host of permissions that we don't want to
# mess with, but which are not very useful from inside the game. While these
# permissions are ok to use, we only show the permissions that we have defined
# in our settings file in order to give better control.
APPS_NOSHOW = ("news","admin","auth","config","contentypes",
"flatpages","news","sessions","sites")
SETTINGS_PERM_NAMES = []
for apps in settings.PERM_ALL_DEFAULTS + settings.PERM_ALL_CUSTOM:
for permtuples in apps:
SETTINGS_PERM_NAMES.append(permtuples[1])
def cmd_setperm(command):
"""
@setperm - set permissions
Usage:
@setperm[/switch] [<user>] = [<permission>]
Switches:
add : add a permission from <user>
del : delete a permission from <user>
list : list all permissions, or those set on <user>
This command sets/clears individual permission bits on a user.
Use /list without any arguments to see all available permissions or those
defined on the <user> argument.
"""
source_object = command.source_object
args = command.command_argument
switches = command.command_switches
if not args:
if "list" not in switches:
source_object.emit_to("Usage: @setperm[/switch] [user] = [permission]")
return
else:
#just print all available permissions
s = "\n---Permission name %s ---Description" % (24 * " ")
permlist = [perm for perm in Permission.objects.all() if perm.content_type.app_label not in APPS_NOSHOW and
perm.name in SETTINGS_PERM_NAMES]
for p in permlist:
app = p.content_type.app_label
if app not in APPS_NOSHOW:
s += "\n%s.%s%s\t%s" % (app, p.codename, (35 - len(app) - len(p.codename)) * " ", p.name)
source_object.emit_to(s)
return
#we have command arguments.
arglist = args.split('=',1)
obj_name = arglist[0].strip()
if not obj_name:
source_object.emit_to("Usage: @setperm[/switch] [user] [= permission]")
return
obj = source_object.search_for_object(obj_name)
if not obj:
return
user = obj.get_user_account()
if not user:
return
if len(arglist) == 1:
#if we didn't have any =, we list the permissions set on <object>.
s = ""
if obj.is_superuser():
s += "\n This is a SUPERUSER account! All permissions are automatically set."
if not user.is_active:
s += "\n ACCOUNT NOT ACTIVE."
if obj.is_staff():
s += "\n Member of staff (can enter Admin interface)"
aperms = user.get_all_permissions()
gperms = user.get_group_permissions()
uperms = [perm for perm in aperms if perm not in gperms]
if gperms:
s += "\n Group-inherited Permissions:"
for p in gperms:
s += "\n --- %s" % p
if uperms:
s += "\n Individually granted Permisssions:"
for p in uperms:
s += "\n ---- %s" % p
if not s:
s = "User %s has no permissions." % obj.get_name()
else:
s = "\nPermissions for user %s: %s" % (obj.get_name(),s)
source_object.emit_to(s)
else:
# we supplied an argument on the form obj = perm
perm_string = arglist[1].strip()
try:
app_label, codename = perm_string.split(".",1)
except ValueError:
source_object.emit_to("Permission should be on the form 'application.permission' .")
return
try:
permission = Permission.objects.filter(content_type__app_label=app_label).get(codename=codename)
except Permission.DoesNotExist:
source_object.emit_to("Permission type '%s' is not a valid permission.\nUse @chperm/list for help with valid permission strings." % perm_string)
return
if not switches:
source_object.emit_to("You must supply a switch /add or /del.")
return
if "add" in switches:
#add the permission to this user
if user.is_superuser:
source_object.emit_to("As a superuser you always have all permissions.")
return
if user.has_perm(perm_string):
source_object.emit_to("User already has this permission.")
return
user.user_permissions.add(permission)
user.save();obj.save()
source_object.emit_to("%s gained the permission '%s'." % (obj.get_name(), permission.name))
obj.emit_to("%s gave you the permission '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
permission.name))
if "del" in switches:
#delete the permission from this user
if user.is_superuser:
source_object.emit_to("As a superuser you always have all permissions.")
return
if not user.has_perm(perm_string):
source_object.emit_to("User is already lacking this permission.")
return
user.user_permissions.remove(permission)
user.save();obj.save()
source_object.emit_to("%s lost the permission '%s'." % (obj.get_name(), permission.name))
obj.emit_to("%s removed your permission '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
permission.name))
GLOBAL_CMD_TABLE.add_command("@setperm", cmd_setperm,
priv_tuple=("auth.change_permission",
"genperms.admin_perm"),
help_category="Admin")
def cmd_setgroup(command):
"""
@setgroup - manage group memberships
Usage:
@setgroup[/switch] [<user>] [= <group>]
Switches:
add - add user to a group
del - remove user from a group
list - list all groups a user is part of, or list all available groups if no user is given
Changes and views the group membership of a user.
"""
source_object = command.source_object
args = command.command_argument
switches = command.command_switches
if not args:
if "list" not in switches:
source_object.emit_to("Usage: @setgroup[/switch] [user] [= permission]")
return
else:
#just print all available permissions
s = "\n---Group name (and grouped permissions):"
for g in Group.objects.all():
s += "\n %s" % g.name
for p in g.permissions.all():
app = p.content_type.app_label
if app not in APPS_NOSHOW:
s += "\n --- %s.%s%s\t%s" % (app, p.codename,
(35 - len(app) - len(p.codename)) * " ", p.name)
source_object.emit_to(s)
return
#we have command arguments.
arglist = args.split('=',1)
obj_name = arglist[0].strip()
if not obj_name:
source_object.emit_to("Usage: @setgroup[/switch] [user] = [permission]")
return
obj = source_object.search_for_object(obj_name)
if not obj:
return
if not obj.is_player():
source_object.emit_to("Only players may be members of permission groups.")
return
user = obj.get_user_account()
if not user:
return
if len(arglist) == 1:
#if we didn't have any =, we list the groups this user is member of
s = ""
if obj.is_superuser():
s += "\n This is a SUPERUSER account! Group membership does not matter."
if not user.is_active:
s += "\n ACCOUNT NOT ACTIVE."
for g in user.groups.all():
s += "\n --- %s" % g
for p in g.permissions.all():
app = p.content_type.app_label
s += "\n -- %s.%s%s\t%s" % (app, p.codename, (35 - len(app) - len(p.codename)) * " ", p.name)
if not s:
s = "User %s is not a member of any groups." % obj.get_name()
else:
s = "\nGroup memberships for user %s: %s" % (obj.get_name(),s)
source_object.emit_to(s)
else:
# we supplied an argument on the form obj = group
group_string = arglist[1].strip()
try:
group = Group.objects.get(name=group_string)
except Group.DoesNotExist:
source_object.emit_to("Group '%s' is not a valid group. Remember that the name is case-sensitive.\nUse @chperm/list for help with valid group names." % group_string)
return
if not switches:
source_object.emit_to("You must supply a switch /add or /del.")
return
if "add" in switches:
#add the user to this group
if user.is_superuser:
source_object.emit_to("As a superuser, group access does not matter.")
return
if user.groups.filter(name=group_string):
source_object.emit_to("User is already a member of this group.")
return
user.groups.add(group)
user.save(); obj.save()
source_object.emit_to("%s added to group '%s'." % (obj.get_name(), group.name))
obj.emit_to("%s added you to the group '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
group.name))
if "del" in switches:
#delete the permission from this user
if user.is_superuser:
source_object.emit_to("As a superuser, group access does not matter.")
return
if not user.groups.filter(name=group_string):
source_object.emit_to("User was not in this group to begin with.")
return
user.groups.remove(group)
user.save(); obj.save()
source_object.emit_to("%s was removed from group '%s'." % (obj.get_name(), group.name))
obj.emit_to("%s removed you from group '%s'." % (source_object.get_name(show_dbref=False,no_ansi=True),
group.name))
GLOBAL_CMD_TABLE.add_command("@setgroup", cmd_setgroup,
priv_tuple=("auth.change_group",
"genperms.admin_group"),
help_category="Admin")
def cmd_sethelp(command):
"""
@sethelp - edit the help database
Usage:
@sethelp[/switches] <topic>[,category][(permissions)][:<text>]
Switches:
add - add or replace a new topic with text.
append - add text to the end of topic.
delete - remove help topic.
force - (used with add) create help topic also if the topic
already exists.
newl - (used with append) add a newline between the old
text and the appended text.
Examples:
@sethelp/add throw : This throws something at ...
@sethelp/add throw, General (genperms.throwing) : This throws ...
@sethelp/add throw : 1st help entry
[[@sethelp_markup]]
@sethelp Help markup
The <text> entry in @sethelp supports markup to automatically divide the help text into
several sub-entries. The beginning of each new entry is marked in the form
[ [Title, category, (privtuple)] ] (with no spaces between the square brackets)
In the markup header, Title is mandatory, the other parts are optional. A new
help entry named Title will be created for each occurence. It is recommended
that the help entries should begin similarly since the system will then identify
them and better handle a list of recommended topics.
"""
source_object = command.source_object
arg = command.command_argument
switches = command.command_switches
if not arg or not switches:
source_object.emit_to("Usage: @sethelp/[add|del|append] <topic>[,category][:<text>]")
return
topicstr = ""
category = ""
text = ""
permtuple = ()
# analyze the argument
arg = arg.split(':', 1)
if len(arg) < 2:
# no : detected; this means we are deleting something.
topicstr = arg[0].strip()
else:
text = arg[1].strip()
# we have 4 possibilities:
# topicstr
# topicstr, category
# topicstr (perm1,perm2,...)
# topicstr, category, (perm1,perm2,...)
arg = arg[0].split('(',1)
if len(arg) > 1:
# we have a perm tuple
arg, permtuple = arg
try:
permtuple = permtuple.strip()[:-1] # cut last ')'
except IndexError:
source_object.emit_to("Malformed permission tuple. %s" % permtuple)
return
permtuple = tuple(permtuple.split(','))
else:
# no perm tuple
arg = arg[0]
arg = arg.split(',', 1)
if len(arg) > 1:
# we have a category
category = arg[1].strip()
topicstr = arg[0].strip()
if 'add' in switches:
# add a new help entry.
if not topicstr or not text:
source_object.emit_to("Usage: @sethelp/add <topic>[,category]:<text>")
return
force_create = ('for' in switches) or ('force' in switches)
topics = helpsystem.edithelp.add_help_manual(source_object, topicstr,
category, text,
permissions=permtuple,
force=force_create)
if not topics:
return
if len(topics) == 1:
string = "The topic already exists. Use /force to overwrite it."
elif len(topics)>1:
string = "The following results are similar to '%s'."
string += " Make sure you are not misspelling, then "
string += "use the /force flag to create a new entry."
string += "\n ".join(topics)
source_object.emit_to(string)
elif 'append' in switches or 'app' in switches:
# add text to the end of a help topic
if not topicstr or not text:
source_object.emit_to("Usage: @sethelp/append <topic>:<text>")
return
# find the topic to append to
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
if not topics:
source_object.emit_to("Help topic '%s' not found." % topicstr)
elif len(topics) > 1:
string = "Multiple matches to this topic. Refine your search."
string += "\n ".join(topics)
else:
# we have exactly one match. Extract all info from it,
# append the text and feed it back into the system.
newtext = topics[0].get_entrytext_ingame()
category = topics[0].category
perm_tuple = topics[0].canview
if perm_tuple:
perm_tuple = tuple(perm for perm in perm_tuple.split(','))
newl = "\n"
if 'newl' in switches or 'newline' in switches:
newl = "\n\n"
newtext += "%s%s" % (newl, text)
topics = helpsystem.edithelp.add_help_manual(source_object,
topicstr,
category,
newtext,
perm_tuple,
force=True)
elif 'del' in switches or 'delete' in switches:
#delete a help entry
topics = helpsystem.edithelp.del_help_manual(source_object, topicstr)
if not topics:
return
else:
string = "Multiple matches for '%s'. Please specify:" % topicstr
string += "\n ".join(topics)
GLOBAL_CMD_TABLE.add_command("@sethelp", cmd_sethelp,
priv_tuple=("helpsys.add_help",
"helpsys.del_help",
"helpsys.admin_heelp"),
help_category="Admin")
def cmd_setcmdalias(command):
"""
@setcmdalias - define shortcuts for commands
Usage:
@setcmdalias[/switch] alias [= command]
Switches:
list - view all command aliases
add - add alias
del - remove and existing alias
This defins a new alias for a common command,
for example like letting 'l' work as
well as 'look'. When you change an alias you must
use @reload/aliases before the alias-change gets
recognized.
"""
source_object = command.source_object
args = command.command_argument
switches = command.command_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.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:
user_input, equiv_command = [arg.strip() for arg in args.split("=",1)]
else:
user_input = args.strip()
if 'add' in switches:
# add alias
edit_aliases.add_alias(user_input, equiv_command)
source_object.emit_to("Alias %s -> %s added. Now do '@reload/aliases'." % (user_input, equiv_command))
return
elif 'del' in switches:
# delete alias
edit_aliases.del_alias(user_input)
source_object.emit_to("Removed alias %s (if it existed). Now do '@reload/aliases'." % user_input)
else:
source_object.emit_to("Usage: @setcmdalias[/switch] [command = ] alias")
GLOBAL_CMD_TABLE.add_command("@setcmdalias", cmd_setcmdalias,
priv_tuple=("genperms.process_control",),
help_category="Admin")
def cmd_delevent(command):
"""
@delevent - remove events manually
Usage:
@delevent[/switch] <pid>
Switch:
force - by default, certain default low-pid system events are protected
from accidental deletion. This switch overrides this
protection.
Removes an event with the given pid (process ID) from the event scheduler.
To see all active events and their pids, use the @ps command.
"""
source_object = command.source_object
switches = command.command_switches
if not command.command_argument:
source_object.emit_to("Usage: @delevent <pid>")
return
try:
pid = int(command.command_argument)
except ValueError:
source_object.emit_to("You must supply a valid pid number.")
return
if pid < 3 and "force" not in switches:
# low-pid protection
string = "Warning:\n"
string += " Pid %i is a low-pid system event. It is usually not\n" % pid
string +=" something you want to delete since crucial\n"
string += " engine functions depend on it. If you were not\n"
string += " mistaken and know what you are doing, give this\n"
string += " command again with the /force switch to override\n"
string += " this protection."
source_object.emit_to(string)
return
event = scheduler.get_event(pid)
if event:
desc = event.description
scheduler.del_event(pid)
source_object.emit_to("Event %i - '%s' removed." % (pid, desc))
else:
source_object.emit_to("No event found with a pid of %i. Use @ps to list process IDs." % pid)
GLOBAL_CMD_TABLE.add_command("@delevent", cmd_delevent,
priv_tuple=("genperms.process_control",),
help_category="Admin")

View file

@ -1,241 +0,0 @@
"""
Implementation of the @search command that resembles MUX2.
"""
from django.db.models import Q
from src.objects.models import Object
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE
def _parse_restriction_split(source_object, restriction_split, search_low_dbnum,
search_high_dbnum):
"""
Parses a split restriction string and sets some needed variables.
Returns a tuple in the form of: (low dbnum, high dbnum)
"""
restriction_size = len(restriction_split)
if restriction_size >= 2:
try:
search_low_dbnum = int(restriction_split[1].strip())
except ValueError:
source_object.emit_to("Invalid value for low dbref limit.")
return False
if restriction_size >= 3:
try:
search_high_dbnum = int(restriction_split[2].strip())
except ValueError:
source_object.emit_to("Invalid value for high dbref limit.")
return False
return search_low_dbnum, search_high_dbnum
def display_results(source_object, search_query):
"""
Display the results to the searcher.
"""
# Lists to hold results by type. There may be a better way to do this
thing_list = []
room_list = []
exit_list = []
player_list = []
for obj in search_query:
if obj.is_thing():
thing_list.append(obj)
elif obj.is_room():
room_list.append(obj)
elif obj.is_exit():
exit_list.append(obj)
elif obj.is_player():
player_list.append(obj)
# Render each section for different object types
if thing_list:
source_object.emit_to("\n\rTHINGS:")
for thing in thing_list:
source_object.emit_to(thing.get_name(show_dbref=True, show_flags=True))
if exit_list:
source_object.emit_to("\n\rEXITS:")
for exit in exit_list:
source_object.emit_to(exit.get_name(show_dbref=True, show_flags=True))
if room_list:
source_object.emit_to("\n\rROOMS:")
for room in room_list:
source_object.emit_to(room.get_name(show_dbref=True, show_flags=True))
if player_list:
source_object.emit_to("\n\rPLAYER:")
for player in player_list:
source_object.emit_to(player.get_name(show_dbref=True, show_flags=True))
# Show the total counts by type
source_object.emit_to("\n\rFound: Rooms...%d Exits...%d Things...%d Players...%d" % (
len(room_list),
len(exit_list),
len(thing_list),
len(player_list)))
def build_query(source_object, search_query, search_player, search_type,
search_restriction, search_low_dbnum, search_high_dbnum):
"""
Builds and returns a QuerySet object, or None if an error occurs.
"""
# Look up an Object matching the player search query
if search_player:
# Replace the string variable with an Object reference
search_player = source_object.search_for_object(search_player)
# Use standard_objsearch to handle duplicate/nonexistant results
if not search_player:
return None
# Searching by player, chain filter
search_query = search_query.filter(owner=search_player)
# Check to ensure valid search types
if search_type == "type":
if search_restriction == "room":
search_query = search_query.filter(type=defines_global.OTYPE_ROOM)
elif search_restriction == "thing":
search_query = search_query.filter(type=defines_global.OTYPE_THING)
elif search_restriction == "exit":
search_query = search_query.filter(type=defines_global.OTYPE_EXIT)
elif search_restriction == "player":
search_query = search_query.filter(type=defines_global.OTYPE_PLAYER)
else:
source_object.emit_to("Invalid class. See 'help SEARCH CLASSES'.")
return None
elif search_type == "parent":
search_query = search_query.filter(script_parent__iexact=search_restriction)
elif search_type == "object" or search_type == "thing":
search_query = search_query.filter(name__icontains=search_restriction,
type=defines_global.OTYPE_THING)
elif search_type == "rooms":
search_query = search_query.filter(name__icontains=search_restriction,
type=defines_global.OTYPE_ROOM)
elif search_type == "exits":
search_query = search_query.filter(name__icontains=search_restriction,
type=defines_global.OTYPE_EXIT)
elif search_type == "players":
search_query = search_query.filter(name__icontains=search_restriction,
type=defines_global.OTYPE_PLAYER)
elif search_type == "zone":
zone_obj = source_object.search_for_object(search_restriction)
# Use search_for_object to handle duplicate/nonexistant results.
if not zone_obj:
return None
search_query = search_query.filter(zone=zone_obj)
elif search_type == "power":
# TODO: Need this once we have powers implemented.
source_object.emit_to("To be implemented...")
return None
elif search_type == "flags":
flag_list = search_restriction.split()
#source_object.emit_to("restriction: %s" % flag_list)
for flag in flag_list:
search_query = search_query.filter(Q(flags__icontains=flag) | Q(nosave_flags__icontains=flag))
if search_low_dbnum:
search_query = search_query.filter(id__gte=search_low_dbnum)
if search_high_dbnum:
search_query = search_query.filter(id__lte=search_high_dbnum)
return search_query
def cmd_search(command):
"""
search
Usage:
search <name>
Searches for owned objects as per MUX2.
"""
source_object = command.source_object
search_player = None
search_type = None
search_restriction = None
search_low_dbnum = None
search_high_dbnum = None
if not command.command_argument:
search_player = "#" + str(source_object.id)
else:
first_check_split = command.command_argument.split(' ', 1)
if '=' in first_check_split[0]:
# @search class=restriction...
eq_split = command.command_argument.split('=', 1)
search_type = eq_split[0]
restriction_split = eq_split[1].split(',')
search_restriction = restriction_split[0].strip()
#source_object.emit_to("@search class=restriction")
#source_object.emit_to("eq_split: %s" % eq_split)
#source_object.emit_to("restriction_split: %s" % restriction_split)
try:
search_low_dbnum, search_high_dbnum = _parse_restriction_split(source_object,
restriction_split,
search_low_dbnum,
search_high_dbnum)
except TypeError:
return
else:
# @search player
if len(first_check_split) == 1:
#source_object.emit_to("@search player")
#source_object.emit_to(first_check_split)
search_player = first_check_split[0]
else:
#source_object.emit_to("@search player class=restriction")
#source_object.emit_to(first_check_split)
search_player = first_check_split[0]
eq_split = first_check_split[1].split('=', 1)
search_type = eq_split[0]
#source_object.emit_to("eq_split: %s" % eq_split)
restriction_split = eq_split[1].split(',')
search_restriction = restriction_split[0]
#source_object.emit_to("restriction_split: %s" % restriction_split)
try:
search_low_dbnum, search_high_dbnum = _parse_restriction_split(source_object,
restriction_split,
search_low_dbnum,
search_high_dbnum)
except TypeError:
return
search_query = Object.objects.all()
#source_object.emit_to("search_player: %s" % search_player)
#source_object.emit_to("search_type: %s" % search_type)
#source_object.emit_to("search_restriction: %s" % search_restriction)
#source_object.emit_to("search_lowdb: %s" % search_low_dbnum)
#source_object.emit_to("search_highdb: %s" % search_high_dbnum)
# Clean up these variables for comparisons.
try:
search_type = search_type.strip().lower()
except AttributeError:
pass
try:
search_restriction = search_restriction.strip().lower()
except AttributeError:
pass
# Build the search query.
search_query = build_query(source_object, search_query, search_player, search_type,
search_restriction, search_low_dbnum,
search_high_dbnum)
# Something bad happened in query construction, die here.
if search_query is None:
return
display_results(source_object, search_query)
GLOBAL_CMD_TABLE.add_command("@search", cmd_search,
priv_tuple=("objects.info",),
help_category="Building")

View file

@ -1,127 +0,0 @@
"""
Commands that are available from the connect screen.
"""
import traceback
from django.contrib.auth.models import User
from src.objects.models import Object
from src import defines_global
from src.util import functions_general
from src.cmdtable import GLOBAL_UNCON_CMD_TABLE
from src.logger import log_errmsg
def cmd_connect(command):
"""
This is the connect command at the connection screen. Fairly simple,
uses the Django database API and User model to make it extremely simple.
"""
session = command.session
# Argument check.
# Fail gracefully if no argument is provided
if not command.command_argument:
session.msg("No arguments provided.\n\r Usage (without <>): connect <email> <password>")
return
arg_list = command.command_argument.split()
if not functions_general.cmd_check_num_args(session, arg_list, 2):
session.msg("Not enough arguments provided.\n\r Usage (without <>): connect <email> <password>")
return
uemail = arg_list[0]
password = arg_list[1]
# Match an email address to an account.
email_matches = Object.objects.get_user_from_email(uemail)
# No username match
if email_matches.count() == 0:
session.msg("The email '%s' does not match any accounts.\n\rIf you are new you should create a new account." % uemail)
return
# We have at least one result, so we can check the password.
user = email_matches[0]
if not user.check_password(password):
session.msg("Incorrect password.")
else:
uname = user.username
session.login(user)
GLOBAL_UNCON_CMD_TABLE.add_command("connect", cmd_connect, auto_help_override=False)
def cmd_create(command):
"""
Handle the creation of new accounts.
"""
session = command.session
# Argument check.
# Fail gracefully if no argument is provided
if not command.command_argument:
session.msg("No arguments provided\n\r Usage (without <>): create \"<username>\" <email> <password>")
return
arg_list = command.command_argument.split()
if not functions_general.cmd_check_num_args(session, arg_list, 2):
session.msg("Too few arguments provided\n\r Usage (without <>): create \"<username>\" <email> <password>")
return
quote_split = command.command_argument.split("\"")
if len(quote_split) < 2:
session.msg("You must enclose your username in quotation marks.")
return
uname = quote_split[1]
lastarg_split = quote_split[2].split()
if len(lastarg_split) != 2:
session.msg("You must specify an email address, followed by a password.")
return
email = lastarg_split[0].strip()
password = lastarg_split[1].strip()
#check so the email is at least on the form xxxx@xxx.xxx
addr = email.split('@')
if len(addr) != 2 or not len(addr[1].split('.')) > 1 or not addr[1].split('.')[-1]:
session.msg("'%s' is not a valid e-mail address." % email)
return
# Search for a user object with the specified username.
account = User.objects.filter(username=uname)
# Match an email address to an account.
email_matches = Object.objects.get_user_from_email(email)
# Look for any objects with an 'Alias' attribute that matches
# the requested username
alias_matches = Object.objects.filter(attribute__attr_name__exact="ALIAS",
attribute__attr_value__iexact=uname).filter(
type=defines_global.OTYPE_PLAYER)
if not account.count() == 0 or not alias_matches.count() == 0:
session.msg("Sorry, there is already a player with that name.")
elif not email_matches.count() == 0:
session.msg("Sorry, there is already a player with that email address.")
elif len(password) < 3:
session.msg("Your password must be at least 3 characters or longer.\n\rFor best security, make it at least 8 characters long, avoid making it a real word and mix numbers into it.")
else:
try:
Object.objects.create_user(command, uname, email, password)
except:
# we have to handle traceback ourself at this point, if we don't errors will givo no feedback.
session.msg("This is a bug. Please e-mail an admin if the problem persists.\n%s" % traceback.format_exc())
log_errmsg(traceback.format_exc())
raise
GLOBAL_UNCON_CMD_TABLE.add_command("create", cmd_create, auto_help_override=False)
def cmd_quit(command):
"""
We're going to maintain a different version of the quit command
here for unconnected users for the sake of simplicity. The logged in
version will be a bit more complicated.
"""
session = command.session
session.msg("Good bye! Disconnecting ...")
session.handle_close()
GLOBAL_UNCON_CMD_TABLE.add_command("quit", cmd_quit, auto_help_override=False)