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:
parent
df29defbcd
commit
f83c2bddf8
222 changed files with 22304 additions and 14371 deletions
|
|
@ -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
364
src/commands/cmdhandler.py
Normal 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
174
src/commands/cmdparser.py
Normal 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
338
src/commands/cmdset.py
Normal 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
|
||||
376
src/commands/cmdsethandler.py
Normal file
376
src/commands/cmdsethandler.py
Normal 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
149
src/commands/command.py
Normal 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))
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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"),
|
||||
|
|
@ -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
|
|
@ -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")
|
||||
|
|
@ -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" )
|
||||
|
||||
|
|
@ -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")
|
||||
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue