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

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

File diff suppressed because one or more lines are too long

View file

@ -5,49 +5,104 @@ EVENNIA SERVER STARTUP SCRIPT
Sets the appropriate environmental variables and launches the server
process. Run the script with the -h flag to see usage information.
"""
from optparse import OptionParser
from subprocess import Popen, call
import os
import sys
import signal
from optparse import OptionParser
from subprocess import Popen, call
# Set the Python path up so we can get to settings.py from here.
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings'
from django.conf import settings
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server.py')
# Determine what the twistd binary name is. Eventually may want to have a
# setting in settings.py to specify the path to the containing directory.
if not os.path.exists('settings.py'):
# make sure we have a settings.py file.
print " No settings.py file found. Launching manage.py ..."
import game.manage
print """
Now configure Evennia by editing your new settings.py file.
If you haven't already, you should also create/configure the
database with 'python manage.py syncdb' before continuing.
When you are ready, run this program again to start the server."""
sys.exit()
# Get the settings
from django.conf import settings
# Setup the launch of twisted depending on which operating system we use
if os.name == 'nt':
TWISTED_BINARY = 'twistd.bat'
try:
# Test for for win32api
import win32api
except ImportError:
print "=" * 78
print """ERROR: Unable to import win32api, which Twisted requires to run. You may
download it from:
print """
ERROR: Unable to import win32api, which Twisted requires to run.
You may download it from:
http://starship.python.net/crew/mhammond/win32/Downloads.html"""
print "=" * 78
http://sourceforge.net/projects/pywin32
or
http://starship.python.net/crew/mhammond/win32/Downloads.html"""
sys.exit()
if not os.path.exists('twistd.bat'):
# Test for executable twisted batch file. This calls the twistd.py
# executable that is usually not found on the path in Windows.
# It's not enough to locate scripts.twistd, what we want is the
# executable script C:\PythonXX/Scripts/twistd.py. Alas we cannot
# hardcode this location since we don't know if user has Python
# in a non-standard location, so we try to figure it out.
from twisted.scripts import twistd
twistd_path = os.path.abspath(
os.path.join(os.path.dirname(twistd.__file__),
os.pardir, os.pardir, os.pardir, os.pardir,
'scripts', 'twistd.py'))
bat_file = open('twistd.bat','w')
bat_file.write("@%s %%*" % twistd_path)
bat_file.close()
print """
INFO: Since you are running Windows, a twistd.bat file was created for you.
The twistd.bat is a simple batch file that tries to call the twisted
executable. The system has determined this to be:
%s
If you should run into errors you might need to edit twistd.bat to point to
the correct location of the Twisted executable (usually called twistd.py).
When you are ready, run this program again to retry the server restart.""" % twistd_path
sys.exit()
TWISTED_BINARY = 'twistd.bat'
else:
TWISTED_BINARY = 'twistd'
# Setup access of the evennia server itself
SERVER_PY_FILE = os.path.join(settings.SRC_DIR, 'server/server.py')
# Add this to the environmental variable for the 'twistd' command.
tmppath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
thispath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if 'PYTHONPATH' in os.environ:
os.environ['PYTHONPATH'] += (":%s" % tmppath)
os.environ['PYTHONPATH'] += (":%s" % thispath)
else:
os.environ['PYTHONPATH'] = tmppath
os.environ['PYTHONPATH'] = thispath
def cycle_logfile():
"""
Move the old log file to evennia.log (by default).
"""
if os.path.exists(settings.DEFAULT_LOG_FILE):
os.rename(settings.DEFAULT_LOG_FILE,
settings.DEFAULT_LOG_FILE+'.old')
Move the old log file to evennia.log.old (by default).
"""
logfile = settings.DEFAULT_LOG_FILE.strip()
logfile_old = logfile + '.old'
if os.path.exists(logfile):
# Cycle the old logfiles to *.old
if os.path.exists(logfile_old):
# E.g. Windows don't support rename-replace
os.remove(logfile_old)
os.rename(logfile, logfile_old)
def start_daemon(parser, options, args):
"""
@ -59,7 +114,8 @@ def start_daemon(parser, options, args):
print "A twistd.pid file exists in the current directory, which suggests that the server is already running."
sys.exit()
print 'Starting in daemon mode...'
print '\nStarting Evennia server in daemon mode ...'
print 'Logging to: %s.' % settings.DEFAULT_LOG_FILE
# Move the old evennia.log file out of the way.
cycle_logfile()
@ -74,7 +130,9 @@ def start_interactive(parser, options, args):
Start in interactive mode, which means the process is foregrounded and
all logging output is directed to stdout.
"""
print 'Starting in interactive mode...'
print '\nStarting Evennia server in interactive mode (stop with keyboard interrupt) ...'
print 'Logging to: Standard output.'
try:
call([TWISTED_BINARY,
'-n',
@ -88,7 +146,7 @@ def stop_server(parser, options, args):
"""
if os.name == 'posix':
if os.path.exists('twistd.pid'):
print 'Stoping the server...'
print 'Stopping the Evennia server...'
f = open('twistd.pid', 'r')
pid = f.read()
os.kill(int(pid), signal.SIGINT)
@ -96,16 +154,18 @@ def stop_server(parser, options, args):
else:
print "No twistd.pid file exists, the server doesn't appear to be running."
elif os.name == 'nt':
print 'TODO not implented'
print '\n\rStopping cannot be done safely under this operating system.'
print 'Kill server using the task manager or shut it down from inside the game.'
else:
print 'Unknown OS delected, can not kill'
print '\n\rUnknown OS detected, can not stop. '
def main():
"""
Beginning of the program logic.
"""
parser = OptionParser(usage="%prog [options] <start|stop>",
description="This command starts or stops the Evennia game server.")
description="This command starts or stops the Evennia game server. Note that you have to setup the database by running 'manage.py syncdb' before starting the server for the first time.")
parser.add_option('-i', '--interactive', action='store_true',
dest='interactive', default=False,
help='Start in interactive mode')

View file

@ -0,0 +1,78 @@
"""
This is the parent class for all Commands in Evennia. Inherit from this and
overload the member functions to define your own commands.
See commands/default/muxcommand.py for an example.
"""
from src.commands.command import Command as BaseCommand
from src.permissions import permissions
from src.utils import utils
class Command(BaseCommand):
"""
Note that the class's __doc__ string (this text) is
used by Evennia to create the automatic help entry for
the command, so make sure to document consistently here.
"""
def has_perm(self, srcobj):
"""
This is called by the cmdhandler to determine
if srcobj is allowed to execute this command. This
also determines if the command appears in help etc.
By default, We use checks of the 'c' type of lock to determine
if the command should be run.
"""
return permissions.has_perm(srcobj, self, 'cmd')
def parse(self):
"""
This method is called by the cmdhandler once the command name
has been identified. It creates a new set of member variables
that can be later accessed from self.func() (see below)
The following variables are available for our use when entering this
method (from the command definition, and assigned on the fly by the
cmdhandler):
self.key - the name of this command ('look')
self.aliases - the aliases of this cmd ('l')
self.permissions - permission string for this command
self.help_category - overall category of command
self.caller - the object calling this command
self.cmdstring - the actual command name used to call this
(this allows you to know which alias was used,
for example)
self.args - the raw input; everything following self.cmdstring.
self.cmdset - the cmdset from which this command was picked. Not
often used (useful for commands like 'help' or to
list all available commands etc)
self.obj - the object on which this command was defined. It is often
the same as self.caller.
"""
pass
def func(self):
"""
This is the hook function that actually does all the work. It is called
by the cmdhandler right after self.parser() finishes, and so has access
to all the variables defined therein.
"""
# a simple test command to show the available properties
string = "-" * 50
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "-" * 50
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
string += "cmd perms (self.permissions): {w%s{n\n" % self.permissions
string += "help category (self.help_category): {w%s{n\n" % self.help_category
string += "object calling (self.caller): {w%s{n\n" % self.caller
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
# show cmdset.key instead of cmdset to shorten output
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
self.caller.msg(string)

View file

@ -0,0 +1,689 @@
"""
Batch processors
These commands implements the 'batch-command' and 'batch-code'
processors, using the functionality in src.utils.batchprocessors.
They allow for offline world-building.
Batch-command is the simpler system. This reads a file (*.ev)
containing a list of in-game commands and executes them in sequence as
if they had been entered in the game (including permission checks
etc).
Example batch-command file: game/gamesrc/commands/examples/example_batch_cmd.ev
Batch-code is a full-fledged python code interpreter that reads blocks
of python code (*.py) and executes them in sequence. This allows for
much more power than Batch-command, but requires knowing Python and
the Evennia API. It is also a severe security risk and should
therefore always be limited to superusers only.
Example batch-code file: game/gamesrc/commands/examples/example_batch_code.py
"""
from traceback import format_exc
from django.conf import settings
from src.utils import batchprocessors
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.commands.cmdset import CmdSet
#global defines for storage
CWHITE = r"%cn%ch%cw"
CRED = r"%cn%ch%cr"
CGREEN = r"%cn%ci%cg"
CYELLOW = r"%cn%ch%cy"
CNORM = r"%cn"
#------------------------------------------------------------
# Helper functions
#------------------------------------------------------------
def batch_cmd_exec(caller):
"""
Helper function for executing a single batch-command entry
"""
ptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
command = stack[ptr]
cmdname = command[:command.find(" ")]
caller.msg("%s %02i/%02i: %s %s%s" % (CGREEN, ptr+1,
len(stack),
cmdname,
CGREEN, " "*(50-len(cmdname))))
try:
caller.execute_cmd(command)
except Exception:
caller.msg(format_exc())
return False
return True
def batch_code_exec(caller):
"""
Helper function for executing a single batch-code entry
"""
ptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
debug = caller.ndb.batch_debug
codedict = stack[ptr]
caller.msg("%s %02i/%02i: %s %s%s" % (CGREEN, ptr + 1,
len(stack),
codedict["firstline"],
CGREEN, " "*(50-len(codedict["firstline"]))))
err = batchprocessors.batch_code_exec(codedict,
extra_environ={"caller":caller}, debug=debug)
if err:
caller.msg(err)
return False
return True
def step_pointer(caller, step=1):
"""
Step in stack, returning the item located.
stackptr - current position in stack
stack - the stack of units
step - how many steps to move from stackptr
"""
ptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
nstack = len(stack)
if ptr + step <= 0:
caller.msg("Beginning of batch file.")
if ptr + step >= nstack:
caller.msg("End of batch file.")
caller.ndb.batch_stackptr = max(0, min(nstack-1, ptr + step))
def show_curr(caller, showall=False):
"Show the current position in stack."
stackptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
if stackptr >= len(stack):
caller.ndb.batch_stackptr = len(stack) - 1
show_curr(caller, showall)
return
entry = stack[stackptr]
if type(entry) == dict:
# we first try the batch-code syntax
firstline = entry['code'][:min(35, len(entry['code'])-1)]
codeall = entry['code']
else:
# we try the batch-cmd syntax instead
firstline = entry[:min(35, len(entry)-1)]
codeall = entry
string = "%s %02i/%02i: %s %s %s %s%s" % (CGREEN,
stackptr+1, len(stack),
firstline, CGREEN,
"(hh for help)",
" "*(35-len(firstline.strip())),
CNORM)
if showall:
string += "\n%s" % codeall
caller.msg(string)
#------------------------------------------------------------
# main access commands
#------------------------------------------------------------
class CmdBatchCommands(MuxCommand):
"""
Build from batch-command file
Usage:
@batchcommands[/interactive] <python path to file>
Switch:
interactive - this mode will offer more control when
executing the batch file, like stepping,
skipping, reloading etc.
Runs batches of commands from a batch-cmd text file (*.ev).
"""
key = "@batchcommands"
aliases = ["@batchcommand", "@batchcmd"]
permissions = "cmd:batchcommands"
help_category = "Building"
def func(self):
"Starts the processor."
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @batchcommands[/interactive] <path.to.file>")
return
python_path = self.args
#parse indata file
commands = batchprocessors.parse_batchcommand_file(python_path)
if not commands:
string = "'%s' not found.\nYou have to supply the python path "
string += "of the file relative to \nyour batch-file directory (%s)."
caller.msg(string % (python_path, settings.BASE_BATCHPROCESS_PATH))
return
switches = self.switches
# Store work data in cache
caller.ndb.batch_stack = commands
caller.ndb.batch_stackptr = 0
caller.ndb.batch_pythonpath = python_path
caller.ndb.batch_batchmode = "batch_commands"
caller.cmdset.add(BatchSafeCmdSet)
if 'inter' in switches or 'interactive' in switches:
# Allow more control over how batch file is executed
# Set interactive state directly
caller.cmdset.add(BatchInteractiveCmdSet)
caller.msg("\nBatch-command processor - Interactive mode for %s ..." % python_path)
show_curr(caller)
else:
caller.msg("Running Batch-command processor - Automatic mode for %s ..." % python_path)
# add the 'safety' cmdset in case the batch processing adds cmdsets to us
for inum in range(len(commands)):
# loop through the batch file
if not batch_cmd_exec(caller):
return
step_pointer(caller, 1)
# clean out the safety cmdset and clean out all other temporary attrs.
caller.cmdset.delete(BatchSafeCmdSet)
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
string = " Batchfile '%s' applied." % python_path
caller.msg("%s%s%s" % (CGREEN, string, " "*(60-len(string))))
class CmdBatchCode(MuxCommand):
"""
Build from batch-code file
Usage:
@batchcode[/interactive] <python path to file>
Switch:
interactive - this mode will offer more control when
executing the batch file, like stepping,
skipping, reloading etc.
debug - auto-delete all objects that has been marked as
deletable in the script file (see example files for
syntax). This is useful so as to to not leave multiple
object copies behind when testing out the script.
Runs batches of commands from a batch-code text file (*.py).
"""
key = "@batchcode"
aliases = ["@batchcodes"]
permissions = "cmd:batchcodes"
help_category = "Building"
def func(self):
"Starts the processor."
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @batchcode[/interactive/debug] <path.to.file>")
return
python_path = self.args
#parse indata file
codes = batchprocessors.parse_batchcode_file(python_path)
if not codes:
string = "'%s' not found.\nYou have to supply the python path "
string += "of the file relative to \nyour batch-file directory (%s)."
caller.msg(string % (python_path, settings.BASE_BATCHPROCESS_PATH))
return
switches = self.switches
debug = False
if 'debug' in switches:
debug = True
# Store work data in cache
caller.ndb.batch_stack = codes
caller.ndb.batch_stackptr = 0
caller.ndb.batch_pythonpath = python_path
caller.ndb.batch_batchmode = "batch_code"
caller.ndb.batch_debug = debug
caller.cmdset.add(BatchSafeCmdSet)
if 'inter' in switches or 'interactive'in switches:
# Allow more control over how batch file is executed
# Set interactive state directly
caller.cmdset.add(BatchInteractiveCmdSet)
caller.msg("\nBatch-code processor - Interactive mode for %s ..." % python_path)
show_curr(caller)
else:
caller.msg("Running Batch-code processor - Automatic mode for %s ..." % python_path)
# add the 'safety' cmdset in case the batch processing adds cmdsets to us
for inum in range(len(codes)):
# loop through the batch file
if not batch_code_exec(caller):
return
step_pointer(caller, 1)
# clean out the safety cmdset and clean out all other temporary attrs.
caller.cmdset.delete(BatchSafeCmdSet)
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
string = " Batchfile '%s' applied." % python_path
caller.msg("%s%s%s" % (CGREEN, string, " "*(60-len(string))))
#------------------------------------------------------------
# State-commands for the interactive batch processor modes
# (these are the same for both processors)
#------------------------------------------------------------
class CmdStateAbort(MuxCommand):
"""
@abort
Exits back the default cmdset, regardless of what state
we are currently in.
"""
key = "@abort"
help_category = "BatchProcess"
def func(self):
"Exit back to default."
caller = self.caller
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
# clear everything but the default cmdset.
caller.cmdset.delete(BatchSafeCmdSet)
caller.cmdset.clear()
caller.msg("Exit: Cleared back to default state.")
class CmdStateLL(MuxCommand):
"""
ll
Look at the full source for the current
command definition.
"""
key = "ll"
help_category = "BatchProcess"
def func(self):
show_curr(self.caller, showall=True)
class CmdStatePP(MuxCommand):
"""
pp
Process the currently shown command definition.
"""
key = "pp"
help_category = "BatchProcess"
def func(self):
"""
This checks which type of processor we are running.
"""
caller = self.caller
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
batch_cmd_exec(caller)
class CmdStateRR(MuxCommand):
"""
rr
Reload the batch file, keeping the current
position in it.
"""
key = "rr"
help_category = "BatchProcess"
def func(self):
caller = self.caller
if caller.ndb.batch_batchmode == "batch_code":
batchprocessors.read_batchcommand_file(caller.ndb.batch_pythonpath)
else:
batchprocessors.read_batchcode_file(caller.ndb.batch_pythonpath)
caller.msg("\nFile reloaded. Staying on same command.\n")
show_curr(caller)
class CmdStateRRR(MuxCommand):
"""
rrr
Reload the batch file, starting over
from the beginning.
"""
key = "rrr"
help_category = "BatchProcess"
def func(self):
caller = self.caller
if caller.ndb.batch_batchmode == "batch_code":
batchprocessors.read_batchcommand_file(caller.ndb.batch_pythonpath)
else:
batchprocessors.read_batchcode_file(caller.ndb.batch_pythonpath)
caller.ndb.batch_stackptr = 0
caller.msg("\nFile reloaded. Restarting from top.\n")
show_curr(caller)
class CmdStateNN(MuxCommand):
"""
nn
Go to next command. No commands are executed.
"""
key = "nn"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
step_pointer(caller, step)
show_curr(caller)
class CmdStateNL(MuxCommand):
"""
nl
Go to next command, viewing its full source.
No commands are executed.
"""
key = "nl"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
step_pointer(caller, step)
show_curr(caller, showall=True)
class CmdStateBB(MuxCommand):
"""
bb
Backwards to previous command. No commands
are executed.
"""
key = "bb"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = -int(self.args)
else:
step = -1
step_pointer(caller, step)
show_curr(caller)
class CmdStateBL(MuxCommand):
"""
bl
Backwards to previous command, viewing its full
source. No commands are executed.
"""
key = "bl"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = -int(self.args)
else:
step = -1
step_pointer(caller, step)
show_curr(caller, showall=True)
class CmdStateSS(MuxCommand):
"""
ss [steps]
Process current command, then step to the next
one. If steps is given,
process this many commands.
"""
key = "ss"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
for istep in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
batch_cmd_exec(caller)
step_pointer(caller, 1)
show_curr(caller)
class CmdStateSL(MuxCommand):
"""
sl [steps]
Process current command, then step to the next
one, viewing its full source. If steps is given,
process this many commands.
"""
key = "sl"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
step = int(self.args)
else:
step = 1
for istep in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
batch_cmd_exec(caller)
step_pointer(caller, 1)
show_curr(caller)
class CmdStateCC(MuxCommand):
"""
cc
Continue to process all remaining
commands.
"""
key = "cc"
help_category = "BatchProcess"
def func(self):
caller = self.caller
nstack = len(caller.ndb.batch_stack)
ptr = caller.ndb.batch_stackptr
step = nstack - ptr
for istep in range(step):
if caller.ndb.batch_batchmode == "batch_code":
batch_code_exec(caller)
else:
batch_cmd_exec(caller)
step_pointer(caller, 1)
show_curr(caller)
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
caller.msg("Finished processing batch file.")
class CmdStateJJ(MuxCommand):
"""
j <command number>
Jump to specific command number
"""
key = "j"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
number = int(self.args)-1
else:
caller.msg("You must give a number index.")
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
step_pointer(caller, step)
show_curr(caller)
class CmdStateJL(MuxCommand):
"""
jl <command number>
Jump to specific command number and view its full source.
"""
key = "jl"
help_category = "BatchProcess"
def func(self):
caller = self.caller
arg = self.args
if arg and arg.isdigit():
number = int(self.args)-1
else:
caller.msg("You must give a number index.")
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
step_pointer(caller, step)
show_curr(caller, showall=True)
class CmdStateQQ(MuxCommand):
"""
qq
Quit the batchprocessor.
"""
key = "qq"
help_category = "BatchProcess"
def func(self):
caller = self.caller
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
caller.cmdset.delete(BatchSafeCmdSet)
caller.cmdset.delete(BatchInteractiveCmdSet)
caller.scripts.validate() # this will clear interactive mode.
caller.msg("Aborted interactive batch mode.")
class CmdStateHH(MuxCommand):
"Help command"
key = "help"
aliases = "hh"
help_category = "BatchProcess"
def func(self):
string = """
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)
"""
self.caller.msg(string)
#------------------------------------------------------------
#
# Defining the cmdsets for the interactive batchprocessor
# mode (same for both processors)
#
#------------------------------------------------------------
class BatchSafeCmdSet(CmdSet):
"""
The base cmdset for the batch processor.
This sets a 'safe' @abort command that will
always be available to get out of everything.
"""
key = "Batch_default"
priority = 104 # override other cmdsets.
def at_cmdset_creation(self):
"Init the cmdset"
self.add(CmdStateAbort())
class BatchInteractiveCmdSet(CmdSet):
"""
The cmdset for the interactive batch processor mode.
"""
key = "Batch_interactive"
priority = 104
def at_cmdset_creation(self):
"init the cmdset"
self.add(CmdStateAbort())
self.add(CmdStateLL())
self.add(CmdStatePP())
self.add(CmdStateRR())
self.add(CmdStateRRR())
self.add(CmdStateNN())
self.add(CmdStateNL())
self.add(CmdStateBB())
self.add(CmdStateBL())
self.add(CmdStateSS())
self.add(CmdStateSL())
self.add(CmdStateCC())
self.add(CmdStateJJ())
self.add(CmdStateJL())
self.add(CmdStateQQ())
self.add(CmdStateHH())

View file

@ -0,0 +1,98 @@
"""
This module ties together all the commands of the default command set.
"""
from src.commands.cmdset import CmdSet
from game.gamesrc.commands.default import general, help, privileged
from game.gamesrc.commands.default import tests, comms, objmanip
from game.gamesrc.commands.default import info, batchprocess
class DefaultCmdSet(CmdSet):
"""
Implements the default command set.
"""
key = "DefaultMUX"
def at_cmdset_creation(self):
"Populates the cmdset"
# The general commands
self.add(general.CmdLook())
self.add(general.CmdPassword())
self.add(general.CmdWall())
self.add(general.CmdInventory())
self.add(general.CmdQuit())
self.add(general.CmdPose())
self.add(general.CmdNick())
self.add(general.CmdEmit())
self.add(general.CmdGet())
self.add(general.CmdDrop())
self.add(general.CmdWho())
self.add(general.CmdSay())
self.add(general.CmdGroup())
# The help system
self.add(help.CmdHelp())
self.add(help.CmdSetHelp())
# Privileged commands
self.add(privileged.CmdReload())
self.add(privileged.CmdPy())
self.add(privileged.CmdListScripts())
self.add(privileged.CmdListCmdSets())
self.add(privileged.CmdListObjects())
self.add(privileged.CmdBoot())
self.add(privileged.CmdDelPlayer())
self.add(privileged.CmdNewPassword())
self.add(privileged.CmdHome())
self.add(privileged.CmdService())
self.add(privileged.CmdShutdown())
self.add(privileged.CmdPerm())
# Info commands
self.add(info.CmdVersion())
self.add(info.CmdTime())
self.add(info.CmdList())
self.add(info.CmdPs())
self.add(info.CmdStats())
# Object manipulation commands
self.add(objmanip.CmdTeleport())
self.add(objmanip.CmdSetObjAlias())
self.add(objmanip.CmdWipe())
self.add(objmanip.CmdSetAttribute())
self.add(objmanip.CmdName())
self.add(objmanip.CmdDesc())
#self.add(objmanip.CmdCpAttr()) #TODO - need testing/debugging
#self.add(objmanip.CmdMvAttr()) #TODO - need testing/debugging
self.add(objmanip.CmdFind())
self.add(objmanip.CmdCopy()) #TODO - need testing/debugging
self.add(objmanip.CmdOpen())
self.add(objmanip.CmdLink())
self.add(objmanip.CmdUnLink())
self.add(objmanip.CmdCreate())
self.add(objmanip.CmdDig())
self.add(objmanip.CmdDestroy())
self.add(objmanip.CmdExamine())
self.add(objmanip.CmdTypeclass())
self.add(objmanip.CmdDebug())
# Comm commands
self.add(comms.CmdAddCom())
self.add(comms.CmdDelCom())
self.add(comms.CmdComlist())
self.add(comms.CmdClist())
self.add(comms.CmdCdestroy())
self.add(comms.CmdChannelCreate())
self.add(comms.CmdCdesc())
self.add(comms.CmdPage())
# Batchprocessor commands
self.add(batchprocess.CmdBatchCommands())
self.add(batchprocess.CmdBatchCode())
# Testing commands
self.add(tests.CmdTest())
self.add(tests.CmdTestState())
self.add(tests.CmdTestPerms())
self.add(tests.TestCom())

View file

@ -0,0 +1,21 @@
"""
This module describes the unlogged state of the default game.
The setting STATE_UNLOGGED should be set to the python path
of the state instance in this module.
"""
from src.commands.cmdset import CmdSet
from game.gamesrc.commands.default import unloggedin
class UnloggedinCmdSet(CmdSet):
"""
Sets up the unlogged cmdset.
"""
key = "Unloggedin"
def at_cmdset_creation(self):
"Populate the cmdset"
self.add(unloggedin.CmdConnect())
self.add(unloggedin.CmdCreate())
self.add(unloggedin.CmdQuit())
self.add(unloggedin.CmdUnconnectedLook())
self.add(unloggedin.CmdUnconnectedHelp())

View file

@ -0,0 +1,792 @@
"""
Comsys command module.
"""
from src.comms.models import Channel, Msg, ChannelConnection
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.utils import create
from src.permissions.permissions import has_perm
def find_channel(caller, channelname):
"""
Helper function for searching for a single channel with
some error handling.
"""
channels = Channel.objects.channel_search(channelname)
if not channels:
caller.msg("Channel '%s' not found." % channelname)
return None
elif len(channels) > 1:
matches = ", ".join(["%s(%s)" % (chan.key, chan.id) for chan in channels])
caller.msg("Multiple channels match (be more specific): \n%s" % matches)
return None
return channels[0]
class CmdAddCom(MuxCommand):
"""
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.
"""
key = "addcom"
help_category = "Comms"
def func(self):
"Implement the command"
caller = self.caller
args = self.args
player = caller.player
if not args:
caller.msg("Usage: addcom [alias =] channelname.")
return
if self.rhs:
# rhs holds the channelname
channelname = self.rhs
alias = self.lhs
else:
channelname = self.args
alias = None
channel = find_channel(caller, channelname)
if not channel:
# we use the custom search method to handle errors.
return
# check permissions
if not has_perm(player, channel, 'chan_listen'):
caller.msg("You are not allowed to listen to this channel.")
return
string = ""
if not channel.has_connection(player):
# we want to connect as well.
if not channel.connect_to(player):
# if this would have returned True, the player is connected
caller.msg("You are not allowed to join this channel.")
return
else:
string += "You now listen to the channel %s. " % channel.key
if alias:
# create a nick and add it to the caller.
nicks = caller.nicks
nicks[alias.strip()] = channel.key
caller.nicks = nicks # nicks auto-save to database.
string += "You can now refer to the channel %s with the alias '%s'."
caller.msg(string % (channel.key, alias))
else:
string += "No alias added."
caller.msg(string)
class CmdDelCom(MuxCommand):
"""
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.
"""
key = "delcom"
help_category = "Comms"
def func(self):
"Implementing the command. "
caller = self.caller
if not self.args:
caller.msg("Usage: delcom <alias>")
return
#find all the nicks defining this channel
searchnick = self.args.lower()
nicks = caller.nicks
channicks = [nick for nick in nicks.keys()
if nick == searchnick]
if not channicks:
caller.msg("You don't have any such alias defined.")
return
#if there are possible nick matches, look if they match a channel.
channel = None
for nick in channicks:
channel = find_channel(caller, nicks[nick])
if channel:
break
if not channel:
caller.msg("No channel with alias '%s' found." % searchnick)
return
player = caller.player
if not channel.has_connection(player):
caller.msg("You are not on that channel.")
else:
if len(channicks) > 1:
del nicks[searchnick]
caller.msg("Your alias '%s' for channel %s was cleared." % (searchnick,
channel.key))
else:
del nicks[searchnick]
channel.disconnect_from(player)
caller.msg("You stop listening to channel '%s'." % channel.key)
# have to save nicks back too
caller.nicks = nicks
class CmdComlist(MuxCommand):
"""
comlist - list channel memberships
Usage:
comlist
Lists the channels a user is subscribed to.
"""
key = "comlist"
aliases = ["channels"]
help_category = "Comms"
def func(self):
"Implement the command"
caller = self.caller
player = caller.player
connections = ChannelConnection.objects.get_all_player_connections(player)
if not connections:
caller.msg("You don't listen to any channels.")
return
# get aliases:
nicks = caller.nicks
channicks = {}
for connection in connections:
channame = connection.channel.key.lower()
channicks[channame] = ", ".join([nick for nick in nicks
if nicks[nick].lower() == channame])
string = "Your subscribed channels (use @clist for full chan list)\n"
string += "** Alias Channel Status\n"
for connection in connections:
string += " %s%s %-15.14s%-22.15s\n" % ('-', '-',
channicks[connection.channel.key.lower()],
connection.channel.key)
string = string[:-1]
caller.msg(string)
# 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.
# """
# caller = self.caller
# arg = self.args
# if not arg:
# cmd_comlist(command)
# caller.msg("(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(self.session)
# aliaslist = chandict.keys()
# aliaslist.sort()
# if arg == "on":
# for alias in aliaslist:
# comsys.plr_chan_on(self.session, alias)
# elif arg == "off":
# for alias in aliaslist:
# comsys.plr_chan_off(self.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=caller.sees_dbrefs())
# for p in filter(lambda o: o.is_player(), objlist)]
# olist = [o.get_name(show_dbref=caller.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]
# caller.msg(s)
# GLOBAL_CMD_TABLE.add_self("allcom", cmd_allcom, help_category="Comms")
## def cmd_clearcom(self):
## """
## 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.
## """
## caller = self.caller
## #get aall subscribed channel memberships
## memberships = caller.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(caller, membership.user_alias)
## comsys.send_cmessage(chan_name, "%s has left the channel." % caller.get_name(show_dbref=False))
## s = s[:-1]
## caller.msg(s)
## GLOBAL_CMD_TABLE.add_self("clearcom", cmd_clearcom)
class CmdClist(MuxCommand):
"""
@clist
Usage:
@clist
list channels
all channels
Lists all available channels in the game.
"""
key = "@clist"
aliases = ["channellist", "all channels"]
help_category = "Comms"
def func(self):
"Implement function"
caller = self.caller
string = "All channels (use comlist to see your subscriptions)\n"
string += "** Channel Perms Description\n"
channels = Channel.objects.get_all_channels()
if not channels:
string += "(No channels) "
for chan in channels:
if has_perm(caller, chan, 'can_listen'):
string += " %s%s %-15.14s%-22.15s%s\n" % \
('-',
'-',
chan.key,
chan.permissions,
#chan.get_owner().get_name(show_dbref=False),
chan.desc)
string = string[:-1]
#s += "** End of Channel List **"
caller.msg(string)
class CmdCdestroy(MuxCommand):
"""
@cdestroy
Usage:
@cdestroy <channel>
Destroys a channel that you control.
"""
key = "@cdestroy"
help_category = "Comms"
def func(self):
"Destroy objects cleanly."
caller = self.caller
if not self.args:
caller.msg("Usage: @cdestroy <channelname>")
return
channel = find_channel(caller, self.args)
if not channel:
caller.msg("Could not find channel %s." % self.args)
return
if not has_perm(caller, channel, 'chan_admin', default_deny=True):
caller.msg("You are not allowed to do that.")
return
message = "Channel %s is being destroyed. Make sure to change your aliases." % channel.key
msgobj = create.create_message(caller, message, channel)
channel.msg(msgobj)
channel.delete()
caller.msg("Channel %s was destroyed." % channel)
## def cmd_cset(self):
## """
## @cset
## Sets various flags on a channel.
## """
## # TODO: Implement cmd_cset
## pass
## def cmd_ccharge(self):
## """
## @ccharge
## Sets the cost to transmit over a channel. Default is free.
## """
## # TODO: Implement cmd_ccharge
## pass
## def cmd_cboot(self):
## """
## @cboot
## Usage:
## @cboot[/quiet] <channel> = <player or object>
## Kicks a player or object from a channel you control.
## """
## caller = self.caller
## args = self.args
## switches = self.self_switches
## if not args or not "=" in args:
## caller.msg("Usage: @cboot[/quiet] <channel> = <object>")
## return
## cname, objname = args.split("=",1)
## cname, objname = cname.strip(), objname.strip()
## if not cname or not objname:
## caller.msg("You must supply both channel and object.")
## return
## try:
## channel = CommChannel.objects.get(name__iexact=cname)
## except CommChannel.DoesNotExist:
## caller.msg("Could not find channel %s." % cname)
## return
## #do we have power over this channel?
## if not channel.controlled_by(caller) or caller.has_perm("channels.channel_admin"):
## caller.msg("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:
## caller.msg("Object '%s' not found." % objname)
## return
## if bootobj.is_player() and not player_boot:
## caller.msg("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:
## caller.msg("'%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." % \
## (caller.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_self("@cboot", cmd_cboot, help_category="Comms")
## def cmd_cemit(self):
## """
## @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_selfs]]
## Useful channel selfs
## (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
## """
## caller = self.caller
## if not self.args:
## caller.msg("@cemit[/switches] <channel> = <message>")
## return
## eq_args = self.args.split('=', 1)
## if len(eq_args) != 2:
## caller.msg("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:
## caller.msg("You must provide a channel name to emit to.")
## return
## if len(cmessage) == 0:
## caller.msg("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:
## caller.msg("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 self.self_switches:
## if not caller.has_perm("objects.emit_commchannel"):
## caller.msg(defines_global.NOPERMS_MSG)
## return
## final_cmessage = cmessage
## show_channel_header = False
## else:
## if "sendername" in self.self_switches:
## if not comsys.plr_has_channel(self.session, cname_parsed,
## return_muted=False):
## caller.msg("You must be on %s to do that." % (cname_parsed,))
## return
## final_cmessage = "%s: %s" % (caller.get_name(show_dbref=False),
## cmessage)
## else:
## if not caller.has_perm("objects.emit_commchannel"):
## caller.msg(defines_global.NOPERMS_MSG)
## return
## final_cmessage = cmessage
## if not "quiet" in self.self_switches:
## caller.msg("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=caller)
## GLOBAL_CMD_TABLE.add_self("@cemit", cmd_cemit,priv_tuple=("channels.emit_commchannel",),
## help_category="Comms")
## def cmd_cwho(self):
## """
## @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 = self.session
## caller = self.caller
## if not self.args:
## cmd_clist(self)
## caller.msg("Usage: @cwho <channel>[/all]")
## return
## channel_name = self.args
## if channel_name.strip() == '':
## caller.msg("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 = caller.has_perm("objects.channel_admin")
## is_controlled_by_plr = name_matches[0].controlled_by(caller)
## if is_controlled_by_plr or is_channel_admin:
## comsys.msg_cwho(caller, channel_name)
## else:
## caller.msg("Permission denied.")
## return
## else:
## caller.msg("No channel with that name was found.")
## return
## GLOBAL_CMD_TABLE.add_self("@cwho", cmd_cwho, help_category="Comms")
class CmdChannelCreate(MuxCommand):
"""
@ccreate
channelcreate
Usage:
@ccreate <new channel>[;alias;alias...] = description
Creates a new channel owned by you.
"""
key = "@ccreate"
aliases = "channelcreate"
permissions = "cmd:ccreate"
help_category = "Comms"
def func(self):
"Implement the command"
caller = self.caller
if not self.args:
caller.msg("Usage @ccreate <channelname>[;alias;alias..] = description")
return
description = ""
if self.rhs:
description = self.rhs
lhs = self.lhs
channame = lhs
aliases = None
if ';' in lhs:
channame, aliases = [part.strip().lower()
for part in lhs.split(';', 1) if part.strip()]
aliases = [alias.strip().lower()
for alias in aliases.split(';') if alias.strip()]
channel = Channel.objects.channel_search(channame)
if channel:
caller.msg("A channel with that name already exists.")
return
# Create and set the channel up
permissions = "chan_send:%s,chan_listen:%s,chan_admin:has_id(%s)" % \
("Players","Players",caller.id)
new_chan = create.create_channel(channame, aliases, description, permissions)
new_chan.connect_to(caller)
caller.msg("Created channel %s and connected to it." % new_chan.key)
## def cmd_cchown(self):
## """
## @cchown
## Usage:
## @cchown <channel> = <player>
## Changes the owner of a channel.
## """
## caller = self.caller
## args = self.args
## if not args or "=" not in args:
## caller.msg("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:
## caller.msg("Channel '%s' not found." % cname)
## return
## #check so we have ownership to give away.
## if not channel.controlled_by(caller) and not caller.has_perm("channels.channel_admin"):
## caller.msg("You don't control this channel.")
## return
## #find the new owner
## new_owner = Object.objects.player_name_search(pname)
## if not new_owner:
## caller.msg("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:
## caller.msg("Owner unchanged.")
## return
## #all is set, change owner
## channel.set_owner(new_owner)
## caller.msg("Owner of %s changed from %s to %s." % (cname, old_pname, pname))
## new_owner.msg("%s transfered ownership of channel '%s' to you." % (old_pname, cname))
## GLOBAL_CMD_TABLE.add_self("@cchown", cmd_cchown, help_category="Comms")
class CmdCdesc(MuxCommand):
"""
@cdesc - set channel description
Usage:
@cdesc <channel> = <description>
Changes the description of the channel as shown in
channel lists.
"""
key = "@cdesc"
permissions = "cmd:cdesc"
help_category = "Comms"
def func(self):
"Implement command"
caller = self.caller
if not self.rhs:
caller.msg("Usage: @cdesc <channel> = <description>")
return
channel = find_channel(caller, self.lhs)
if not channel:
caller.msg("Channel '%s' not found." % self.lhs)
return
#check permissions
if not has_perm(caller, channel, 'channel_admin'):
caller.msg("You cant admin this channel.")
return
# set the description
channel.desc = self.rhs
channel.save()
caller.msg("Description of channel '%s' set to '%s'." % (channel.key, self.rhs))
class CmdPage(MuxCommand):
"""
page - send private message
Usage:
page[/switches] [<player>,<player>,... = <message>]
tell ''
Switch:
list - show your last 10 tells/pages.
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.
"""
key = "page"
aliases = ['tell']
permissions = "cmd:tell"
help_category = "Comms"
def func(self):
"Implement function using the Msg methods"
caller = self.caller
player = caller.player
# get the last message we sent
messages = list(Msg.objects.get_messages_by_sender(player))
pages = [msg for msg in messages
if msg.receivers]
if pages:
lastpage = pages[-1]
if 'list' in self.switches:
if len(messages) > 10:
lastpages = messages[-10:]
else:
lastpages = messages
lastpages = "\n ".join(["%s to %s: %s" % (mess.date_sent, mess.receivers.all(),
mess.message)
for mess in messages])
caller.msg("Your latest pages:\n %s" % lastpages )
return
if not self.args or not self.rhs:
if pages:
string = "You last paged %s." % (", ".join([obj.name
for obj in lastpage.receivers.all()]))
caller.msg(string)
return
else:
string = "You haven't paged anyone yet."
caller.msg(string)
return
# Build a list of targets
if not self.lhs:
# If there are no targets, then set the targets
# to the last person they paged.
receivers = lastpage.receivers
else:
receivers = self.lhslist
recobjs = []
for receiver in receivers:
obj = caller.search("*%s" % (receiver.lstrip('*')), global_search=True)
if not obj:
return
recobjs.append(obj)
header = "{wPlayer{n {c%s{n {wpages:{n" % caller.key
message = self.rhs
# create the persistent message object
msg = create.create_message(caller, message,
receivers=recobjs)
# tell the players they got a message.
for obj in recobjs:
obj.msg("%s %s" % (header, message))
caller.msg("You paged %s with '%s'." % (recobjs, message))

View file

@ -0,0 +1,703 @@
"""
Generic command module. Pretty much every command should go here for
now.
"""
import time
from src.server import sessionhandler
from src.permissions.models import PermissionGroup
from src.permissions.permissions import has_perm, has_perm_string
from src.objects.models import HANDLE_SEARCH_ERRORS
from src.utils import utils
from game.gamesrc.commands.default.muxcommand import MuxCommand
class CmdLook(MuxCommand):
"""
look
Usage:
look
look <obj>
Observes your location or objects in your vicinity.
"""
key = "look"
aliases = ["l"]
def func(self):
"""
Handle the looking.
"""
caller = self.caller
args = self.args # caller.msg(inp)
if args:
# Use search to handle duplicate/nonexistant results.
looking_at_obj = caller.search(args)
if not looking_at_obj:
return
else:
looking_at_obj = caller.location
if not looking_at_obj:
caller.msg("Location: None")
return
# get object's appearance
caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method.
looking_at_obj.at_desc(looker=caller)
class CmdPassword(MuxCommand):
"""
@password - set your password
Usage:
@password <old password> = <new password>
Changes your password. Make sure to pick a safe one.
"""
key = "@password"
def func(self):
"hook function."
caller = self.caller
if not self.rhs:
caller.msg("Usage: @password <oldpass> = <newpass>")
return
oldpass = self.lhslist[0] # this is already stripped by parse()
newpass = self.rhslist[0] # ''
try:
uaccount = caller.user
except AttributeError:
caller.msg("This is only applicable for players.")
return
if not uaccount.check_password(oldpass):
caller.msg("The specified old password isn't correct.")
elif len(newpass) < 3:
caller.msg("Passwords must be at least three characters long.")
else:
uaccount.set_password(newpass)
uaccount.save()
caller.msg("Password changed.")
class CmdNick(MuxCommand):
"""
Define a personal alias/nick
Usage:
alias[/switches] <alias> = [<string>]
nick ''
Switches:
obj - alias an object
player - alias a player
clearall - clear all your aliases
list - show all defined aliases
If no switch is given, a command/channel alias is created, used
to replace strings before sending the command.
Creates a personal nick for some in-game object or
string. When you enter that string, it will be replaced
with the alternate string. The switches dictate in what
situations the nick is checked and substituted. If string
is None, the alias (if it exists) will be cleared.
Obs - no objects are actually changed with this command,
if you want to change the inherent aliases of an object,
use the @alias command instead.
"""
key = "alias"
aliases = ["nick"]
def func(self):
"Create the nickname"
caller = self.caller
switches = self.switches
if 'list' in switches:
string = "{wAliases:{n \n"
string = string + "\n\r".join(["%s = %s" % (alias, replace)
for alias, replace
in caller.nicks.items()])
caller.msg(string)
return
if 'clearall' in switches:
del caller.nicks
caller.msg("Cleared all aliases.")
return
if not self.args or not self.lhs:
caller.msg("Usage: alias[/switches] string = [alias]")
return
alias = self.lhs
rstring = self.rhs
err = None
if rstring == alias:
err = "No point in setting alias same as the string to replace..."
caller.msg(err)
return
elif 'obj' in switches:
# object alias, for adressing objects
# (including user-controlled ones)
err = caller.set_nick("_obj:%s" % alias, rstring)
atype = "Object"
elif 'player' in switches:
# player alias, used for messaging
err = caller.set_nick("_player:%s" % alias, rstring)
atype = "Player "
else:
# a command/channel alias - these are replaced if
# they begin a command string.
caller.msg(rstring)
caller.msg("going in: %s %s" % (alias, rstring))
err = caller.set_nick(alias, rstring)
atype = "Command/channel "
if err:
if rstring:
err = "%salias %s changed from '%s' to '%s'." % (atype, alias, err, rstring)
else:
err = "Cleared %salias '%s'(='%s')." % (atype, alias, err)
else:
err = "Set %salias '%s' = '%s'" % (atype, alias, rstring)
caller.msg(err.capitalize())
class CmdEmit(MuxCommand):
"""
@emit
Usage:
@emit[/switches] [<obj>, <obj>, ... =] <message>
@remit [<obj>, <obj>, ... =] <message>
@pemit [<obj>, <obj>, ... =] <message>
Switches:
room : limit emits to rooms only
players : limit emits to players only
contents : send to the contents of matched objects too
Emits a message to the selected objects or to
your immediate surroundings. If the object is a room,
send to its contents. @remit and @pemit are just
limited forms of @emit, for sending to rooms and
to players respectively.
"""
key = "@emit"
aliases = ["@pemit", "@remit"]
permissions = "cmd:emit"
help_category = "Comms"
def func(self):
"Implement the command"
caller = self.caller
args = self.args
if not args:
string = "Usage: "
string += "\n@emit[/switches] [<obj>, <obj>, ... =] <message>"
string += "\n@remit [<obj>, <obj>, ... =] <message>"
string += "\n@pemit [<obj>, <obj>, ... =] <message>"
caller.msg(string)
return
rooms_only = 'rooms' in self.switches
players_only = 'players' in self.switches
send_to_contents = 'contents' in self.switches
# we check which command was used to force the switches
if self.cmdstring == '@remit':
rooms_only = True
elif self.cmdstring == '@pemit':
players_only = True
if not self.rhs:
message = self.args
objnames = [caller.location.key]
else:
message = self.rhs
objnames = self.lhslist
# send to all objects
for objname in objnames:
obj = caller.search(objname, global_search=True)
if not obj:
return
if rooms_only and not obj.location == None:
caller.msg("%s is not a room. Ignored." % objname)
continue
if players_only and not obj.has_player:
caller.msg("%s has no active player. Ignored." % objname)
continue
if has_perm(caller, obj, 'send_to'):
obj.msg(message)
if send_to_contents:
for content in obj.contents:
content.msg(message)
caller.msg("Emitted to %s and its contents." % objname)
else:
caller.msg("Emitted to %s." % objname)
else:
caller.msg("You are not allowed to send to %s." % objname)
class CmdWall(MuxCommand):
"""
@wall
Usage:
@wall <message>
Announces a message to all connected players.
"""
key = "@wall"
permissions = "cmd:wall"
def func(self):
"Implements command"
if not self.args:
self.caller.msg("Usage: @wall <message>")
return
message = "%s shouts \"%s\"" % (self.caller.name, self.args)
sessionhandler.announce_all(message)
class CmdInventory(MuxCommand):
"""
inventory
Usage:
inventory
inv
Shows a player's inventory.
"""
key = "inventory"
aliases = ["inv", "i"]
def func(self):
"hook function"
string = "You are carrying:"
for item in self.caller.contents:
string += "\n %s" % item.name
self.caller.msg(string)
## money = int(caller.MONEY)
## if money == 1:
## money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_SINGULAR")
## else:
## money_name = ConfigValue.objects.get_configvalue("MONEY_NAME_PLURAL")
##caller.msg("You have %d %s." % (money, money_name))
class CmdGet(MuxCommand):
"""
get
Usage:
get <obj>
Picks up an object from your location and puts it in
your inventory.
"""
key = "get"
aliases = "grab"
def func(self):
"implements the command."
caller = self.caller
if not self.args:
caller.msg("Get what?")
return
obj = caller.search(self.args)
if not obj:
return
if caller == obj:
caller.msg("You can't get yourself.")
return
if obj.player or obj.db._destination:
# don't allow picking up player objects, nor exits.
caller.msg("You can't get that.")
return
if not has_perm(caller, obj, 'get'):
#TODO - have the object store fail messages?
caller.msg("You can't get that.")
return
obj.move_to(caller, quiet=True)
caller.msg("You pick up %s." % obj.name)
caller.location.msg_contents("%s picks up %s." %
(caller.name,
obj.name),
exclude=caller)
# calling hook method
obj.at_get(caller)
class CmdDrop(MuxCommand):
"""
drop
Usage:
drop <obj>
Lets you drop an object from your inventory into the
location you are currently in.
"""
key = "drop"
def func(self):
"Implement command"
caller = self.caller
if not self.args:
caller.msg("Drop what?")
return
results = caller.search(self.args, ignore_errors=True)
# we process the results ourselves since we want to sift out only
# those in our inventory.
results = [obj for obj in results if obj in caller.contents]
# now we send it into the handler.
obj = HANDLE_SEARCH_ERRORS(caller, self.args, results, False)
if not obj:
return
obj.move_to(caller.location, quiet=True)
caller.msg("You drop %s." % (obj.name,))
caller.location.msg_contents("%s drops %s." %
(caller.name, obj.name),
exclude=caller)
# Call the object script's at_drop() method.
obj.at_drop(caller)
class CmdQuit(MuxCommand):
"""
quit
Usage:
quit
Gracefully disconnect from the game.
"""
key = "quit"
def func(self):
"hook function"
sessions = self.caller.sessions
for session in sessions:
session.msg("Quitting. Hope to see you soon again.")
session.handle_close()
class CmdWho(MuxCommand):
"""
who
Usage:
who
doing
Shows who is currently online. Doing is an
alias that limits info also for those with
all permissions.
"""
key = "who"
aliases = "doing"
def func(self):
"""
Get all connected players by polling session.
"""
caller = self.caller
session_list = sessionhandler.get_sessions()
if self.cmdstring == "doing":
show_session_data = False
else:
show_session_data = has_perm_string(caller, "Immortals,Wizards")
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 session in session_list:
if not session.logged_in:
continue
delta_cmd = time.time() - session.cmd_last_visible
delta_conn = time.time() - session.conn_time
plr_pobject = session.get_character()
if show_session_data:
retval += '%-31s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n' % \
(plr_pobject.name[:25], \
# On-time
utils.time_format(delta_conn,0), \
# Idle time
utils.time_format(delta_cmd,1), \
# Flags
'', \
# Location
plr_pobject.location.id, \
session.cmd_total, \
# More flags?
'', \
session.address[0])
else:
retval += '%-31s%9s %4s%-3s\r\n' % \
(plr_pobject.name[:25], \
# On-time
utils.time_format(delta_conn,0), \
# Idle time
utils.time_format(delta_cmd,1), \
# Flags
'')
retval += '%d Players logged in.' % (len(session_list),)
caller.msg(retval)
class CmdSay(MuxCommand):
"""
say
Usage:
say <message>
Talk to those in your current location.
"""
key = "say"
def func(self):
"Run the say command"
caller = self.caller
if not self.args:
caller.msg("Say what?")
return
speech = self.args
# calling the speech hook on the location
speech = caller.location.at_say(caller, speech)
# Feedback for the object doing the talking.
caller.msg("You say, '%s'" % speech)
# Build the string to emit to neighbors.
emit_string = "{c%s{n says, '%s'" % (caller.name,
speech)
caller.location.msg_contents(emit_string,
exclude=caller)
## def cmd_fsay(command):
## """
## @fsay - make an object say something
## Usage:
## @fsay <obj> = <text to say>
## Make an object talk to its current location.
## """
## caller = command.caller
## args = command.command_argument
## if not args or not "=" in args:
## caller.msg("Usage: @fsay <obj> = <text to say>")
## return
## target, speech = [arg.strip() for arg in args.split("=",1)]
## # find object
## if target in ['here']:
## results = [caller.location]
## elif target in ['me','my']:
## results = [caller]
## else:
## results = Object.objects.global_object_name_search(target)
## if not results:
## caller.msg("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.name
## caller.msg(string)
## return
## target = results[0]
## # permission check
## if not caller.controls_other(target):
## caller.msg("Cannot pose %s (you don's control it)" % target.name)
## return
## # Feedback for the object doing the talking.
## caller.msg("%s says, '%s%s'" % (target.name,
## speech,
## ANSITable.ansi['normal']))
## # Build the string to emit to neighbors.
## emit_string = "%s says, '%s'" % (target.name,
## speech)
## target.location.msg_contents(emit_string,
## exclude=caller)
## GLOBAL_CMD_TABLE.add_command("@fsay", cmd_fsay)
class CmdPose(MuxCommand):
"""
pose - strike a pose
Usage:
pose <pose text>
pose's <pose text>
Example:
pose is standing by the wall, smiling.
-> others will see:
Tom is standing by the wall, smiling.
Describe an script being taken. The pose text will
automatically begin with your name.
"""
key = "pose"
aliases = [":", "emote"]
def parse(self):
"""
Custom parse the cases where the emote
starts with some special letter, such
as 's, at which we don't want to separate
the caller's name and the emote with a
space.
"""
args = self.args
if args and not args[0] in ["'", ",", ":"]:
args = " %s" % args
self.args = args
def func(self):
"Hook function"
if not self.args:
msg = "Do what?"
else:
msg = "%s%s" % (self.caller.name, self.args)
self.caller.location.msg_contents(msg)
## 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.
## """
## caller = command.caller
## args = command.command_argument
## if not args or not "=" in args:
## caller.msg("Usage: @fpose <obj> = <pose text>")
## return
## target, pose_string = [arg.strip() for arg in args.split("=",1)]
## # find object
## if target in ['here']:
## results = [caller.location]
## elif target in ['me','my']:
## results = [caller]
## else:
## results = Object.objects.global_object_name_search(target)
## if not results:
## caller.msg("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.name
## caller.msg(string)
## return
## target = results[0]
## # permission check
## if not caller.controls_other(target):
## caller.msg("Cannot pose %s (you don's control it)" % target.name)
## return
## if "nospace" in command.command_switches:
## # Output without a space between the player name and the emote.
## sent_msg = "%s%s" % (target.name,
## pose_string)
## else:
## # No switches, default.
## sent_msg = "%s %s" % (target.name,
## pose_string)
## caller.location.msg_contents(sent_msg)
## GLOBAL_CMD_TABLE.add_command("@fpose", cmd_fpose)
class CmdGroup(MuxCommand):
"""
group - show your groups
Usage:
group
This command shows you which user permission groups
you are a member of, if any.
"""
key = "group"
aliases = "groups"
def func(self):
"Load the permission groups"
caller = self.caller
string = ""
if caller.player and caller.player.is_superuser:
string += "\n This is a SUPERUSER account! Group membership does not matter."
else:
# get permissions and determine if they are groups
perms = [perm.strip().lower() for perm in caller.player.permissions
if perm.strip()]
for group in [group for group in PermissionGroup.objects.all()
if group.key.lower() in perms]:
string += "\n %s\t\t%s" % (group.key, [str(perm) for perm in group.group_permissions])
if string:
string = "\nYour (%s's) group memberships: %s" % (caller.name, string)
else:
string = "\nYou are not not a member of any groups."
caller.msg(string)
## 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.caller.execute_cmd("help/apropos %s" % arg)
## GLOBAL_CMD_TABLE.add_command("apropos", cmd_apropos)
## GLOBAL_CMD_TABLE.add_command("suggest", cmd_apropos)

View file

@ -0,0 +1,281 @@
"""
The help command. The basic idea is that help texts for commands
are best written by those that write the commands - the admins. So
command-help is all auto-loaded and searched from the current command
set. The normal, database-tied help system is used for collaborative
creation of other help topics such as RP help or game-world aides.
"""
from src.utils.utils import fill, dedent
from src.commands.command import Command
from src.help.models import HelpEntry
from src.permissions.permissions import has_perm
from src.utils import create
from game.gamesrc.commands.default.muxcommand import MuxCommand
LIST_ARGS = ["list", "all"]
def format_help_entry(title, help_text, aliases=None,
suggested=None):
"This visually formats the help entry."
string = "-"*70 + "\n"
if title:
string += "Help topic for {w%s{n" % (title.capitalize())
if aliases:
string += " (aliases: %s)" % (", ".join(aliases))
if help_text:
string += "\n%s" % dedent(help_text.rstrip())
if suggested:
string += "\nSuggested:\n"
string += fill(", ".join(suggested))
string.strip()
string += "\n" + "-"*70
return string
def format_help_list(hdict_cmds, hdict_db):
"Output a category-ordered list"
string = "\n\r" + "-"*70 + "\n\r {gCommand help entries{n\n" + "-"*70
for category in sorted(hdict_cmds.keys()):
string += "\n {w%s{n:\n" % \
(str(category).capitalize())
string += fill(", ".join(sorted(hdict_cmds[category])))
if hdict_db:
string += "\n\r\n\r" + "-"*70 + "\n\r {gOther help entries{n\n" + '-'*70
for category in sorted(hdict_db.keys()):
string += "\n\r {w%s{n:\n" % (str(category).capitalize())
string += fill(", ".join(sorted([str(topic) for topic in hdict_db[category]])))
return string
class CmdHelp(Command):
"""
The main help command
Usage:
help <topic or command>
help list
help all
This will search for help on commands and other
topics related to the game.
"""
key = "help"
# this is a special cmdhandler flag that makes the cmdhandler also pack
# the current cmdset with the call to self.func().
return_cmdset = True
def parse(self):
"""
inp is a string containing the command or topic match.
"""
self.args = self.args.strip().lower()
def func(self):
"""
Run the dynamic help entry creator.
"""
query, cmdset = self.args, self.cmdset
caller = self.caller
if not query:
query = "all"
# Listing help entries
if query in LIST_ARGS:
# we want to list all available help entries
hdict_cmd = {}
for cmd in (cmd for cmd in cmdset if has_perm(caller, cmd, 'cmd')
if not cmd.key.startswith('__')
and not (hasattr(cmd, 'is_exit') and cmd.is_exit)):
if hdict_cmd.has_key(cmd.help_category):
hdict_cmd[cmd.help_category].append(cmd.key)
else:
hdict_cmd[cmd.help_category] = [cmd.key]
hdict_db = {}
for topic in (topic for topic in HelpEntry.objects.get_all_topics()
if has_perm(caller, topic, 'view')):
if hdict_db.has_key(topic.help_category):
hdict_db[topic.help_category].append(topic.key)
else:
hdict_db[topic.help_category] = [topic.key]
help_entry = format_help_list(hdict_cmd, hdict_db)
caller.msg(help_entry)
return
# Look for a particular help entry
# Cmd auto-help dynamic entries
cmdmatches = [cmd for cmd in cmdset
if query in cmd and has_perm(caller, cmd, 'cmd')]
if len(cmdmatches) > 1:
# multiple matches. Try to limit it down to exact match
exactmatches = [cmd for cmd in cmdmatches if cmd == query]
if exactmatches:
cmdmatches = exactmatches
# Help-database static entries
dbmatches = \
[topic for topic in
HelpEntry.objects.find_topicmatch(query, exact=False)
if has_perm(caller, topic, 'view')]
if len(dbmatches) > 1:
exactmatches = \
[topic for topic in
HelpEntry.objects.find_topicmatch(query, exact=True)
if has_perm(caller, topic, 'view')]
if exactmatches:
dbmatches = exactmatches
# Handle result
if (not cmdmatches) and (not dbmatches):
# no normal match. Check if this is a category match instead
categ_cmdmatches = [cmd for cmd in cmdset
if query == cmd.help_category and has_perm(caller, cmd, 'cmd')]
categ_dbmatches = \
[topic for topic in
HelpEntry.objects.find_topics_with_category(query)
if has_perm(caller, topic, 'view')]
if categ_cmdmatches or categ_dbmatches:
help_entry = format_help_list({query:categ_cmdmatches},
{query:categ_dbmatches})
else:
help_entry = "No help entry found for '%s'" % query
elif len(cmdmatches) == 1:
# we matched against a command name or alias. Show its help entry.
suggested = []
if dbmatches:
suggested = [entry.key for entry in dbmatches]
cmd = cmdmatches[0]
help_entry = format_help_entry(cmd.key, cmd.__doc__,
aliases=cmd.aliases,
suggested=suggested)
elif len(dbmatches) == 1:
# matched against a database entry
entry = dbmatches[0]
help_entry = format_help_entry(entry.key, entry.entrytext)
else:
# multiple matches of either type
cmdalts = [cmd.key for cmd in cmdmatches]
dbalts = [entry.key for entry in dbmatches]
helptext = "Multiple help entries match your search ..."
help_entry = format_help_entry("", helptext, None, cmdalts + dbalts)
# send result to user
caller.msg(help_entry)
class CmdSetHelp(MuxCommand):
"""
@sethelp - edit the help database
Usage:
@sethelp[/switches] <topic>[,category[,permission,permission,...]] = <text>
Switches:
add - add or replace a new topic with text.
append - add text to the end of topic with a newline between.
merge - As append, but don't add a newline between the old
text and the appended text.
delete - remove help topic.
force - (used with add) create help topic also if the topic
already exists.
Examples:
@sethelp/add throw = This throws something at ...
@sethelp/append pickpocketing,Thievery,is_thief, is_staff) = This steals ...
@sethelp/append pickpocketing, ,is_thief, is_staff) = This steals ...
"""
key = "@sethelp"
permissions = "cmd:sethelp"
help_category = "Building"
def func(self):
"Implement the function"
caller = self.caller
switches = self.switches
lhslist = self.lhslist
rhs = self.rhs
if not self.rhs:
caller.msg("Usage: @sethelp/[add|del|append|merge] <topic>[,category[,permission,..] = <text>]")
return
topicstr = ""
category = ""
permissions = ""
try:
topicstr = lhslist[0]
category = lhslist[1]
permissions = ",".join(lhslist[2:])
except Exception:
pass
if not topicstr:
caller.msg("You have to define a topic!")
return
string = ""
print topicstr, category, permissions
if switches and switches[0] in ('append', 'app','merge'):
# add text to the end of a help topic
# find the topic to append to
old_entry = None
try:
old_entry = HelpEntry.objects.get(key=topicstr)
except Exception:
pass
if not old_entry:
string = "Could not find topic '%s'. You must give an exact name." % topicstr
else:
entrytext = old_entry.entrytext
if switches[0] == 'merge':
old_entry.entrytext = "%s %s" % (entrytext, self.rhs)
string = "Added the new text right after the old one (merge)."
else:
old_entry.entrytext = "%s\n\n%s" % (entrytext, self.rhs)
string = "Added the new text as a new paragraph after the old one (append)"
old_entry.save()
elif switches and switches[0] in ('delete','del'):
#delete a help entry
old_entry = None
try:
old_entry = HelpEntry.objects.get(key=topicstr)
except Exception:
pass
if not old_entry:
string = "Could not find topic. You must give an exact name."
else:
old_entry.delete()
string = "Deleted the help entry '%s'." % topicstr
else:
# add a new help entry.
force_create = ('for' in switches) or ('force' in switches)
old_entry = None
try:
old_entry = HelpEntry.objects.get(key=topicstr)
except Exception:
pass
if old_entry:
if force_create:
old_entry.key = topicstr
old_entry.entrytext = self.rhs
old_entry.help_category = category
old_entry.permissions = permissions
old_entry.save()
string = "Overwrote the old topic '%s' with a new one." % topicstr
else:
string = "Topic '%s' already exists. Use /force to overwrite it." % topicstr
else:
# no old entry. Create a new one.
new_entry = create.create_help_entry(topicstr,
rhs, category, permissions)
if new_entry:
string = "Topic '%s' was successfully created." % topicstr
else:
string = "Error when creating topic '%s'! Maybe it already exists?" % topicstr
# give feedback
caller.msg(string)

View file

@ -0,0 +1,204 @@
"""
Commands that are generally staff-oriented that show information regarding
the server instance.
"""
import os
import django, twisted
from django.contrib.auth.models import User
from src.objects.models import ObjectDB
from src.scripts.models import ScriptDB
from src.utils import utils
from src.utils import gametime
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.commands import cmdsethandler
class CmdVersion(MuxCommand):
"""
@version - game version
Usage:
@version
Display the game version info.
"""
key = "@version"
help_category = "System"
def func(self):
"Show the version"
version = utils.get_evennia_version()
string = "-"*50 +"\n\r"
string += " Evennia %s\n\r" % version
string += " (Django %s, " % (django.get_version())
string += " Twisted %s)\n\r" % (twisted.version.short())
string += "-"*50
self.caller.msg(string)
class CmdTime(MuxCommand):
"""
@time
Usage:
@time
Server local time.
"""
key = "@time"
aliases = "@uptime"
permissions = "cmd:time"
help_category = "System"
def func(self):
"Show times."
string2 = "\nCurrent server uptime:\n %i yrs, %i months, "
string2 += "%i weeks, %i days, %i hours, %i minutes and %i secs."
string2 = string2 % gametime.uptime(format=True)
string3 = "\nTotal running time (gametime x %g):" % (1.0/gametime.TIMEFACTOR)
string3 += "\n %i yrs, %i months, %i weeks, %i days, "
string3 += "%i hours, %i minutes and %i secs."
string3 = string3 % gametime.runtime(format=True)
#print "runtime:", gametime.runtime()
string1 = "\nTotal game time (realtime x %g):" % (gametime.TIMEFACTOR)
string1 += "\n %i yrs, %i months, %i weeks, %i days, "
string1 += "%i hours, %i minutes and %i secs."
string1 = string1 % (gametime.gametime(format=True))
#print "gametime:", gametime.gametime()
string4 = ""
if not utils.host_os_is('nt'):
# os.getloadavg() is not available on Windows.
loadavg = os.getloadavg()
string4 = "\n Server load (1 min) : %g%%" % (100 * loadavg[0])
string = "%s%s%s%s" % (string2, string3, string1, string4)
self.caller.msg(string)
class CmdList(MuxCommand):
"""
@list - list info
Usage:
@list commands | process
Shows game related information depending
on which argument is given.
"""
key = "@list"
permissions = "cmd:list"
help_category = "System"
def func(self):
"Show list."
caller = self.caller
if not self.args:
caller.msg("Usage: @list commands|process")
return
string = ""
if self.arglist[0] in ["com", "command", "commands"]:
string = "Command sets currently in cache:"
for cmdset in cmdsethandler.get_cached_cmdsets():
string += "\n %s" % cmdset
elif self.arglist[0] in ["proc","process"]:
if utils.host_os_is('nt'):
string = "Feature not available on Windows."
else:
import resource
loadavg = os.getloadavg()
string = "\n Server load (1 min) : %.2f " % loadavg[0]
psize = resource.getpagesize()
rusage = resource.getrusage(resource.RUSAGE_SELF)
string += "\n Process ID: %10d" % os.getpid()
string += "\n Bytes per page: %10d" % psize
string += "\n Time used: %10d, user: %g" % (rusage[0], rusage[1])
string += "\n Integral mem: %10d shared, %10d, private, %10d stack " % \
(rusage[3], rusage[4], rusage[5])
string += "\n Max res mem: %10d pages %10d bytes" % \
(rusage[2],rusage[2] * psize)
string += "\n Page faults: %10d hard %10d soft %10d swapouts " % \
(rusage[7], rusage[6], rusage[8])
string += "\n Disk I/O: %10d reads %10d writes " % \
(rusage[9], rusage[10])
string += "\n Network I/O: %10d in %10d out " % \
(rusage[12], rusage[11])
string += "\n Context swi: %10d vol %10d forced %10d sigs " % \
(rusage[14], rusage[15], rusage[13])
else:
string = "Not a valid option."
# send info
caller.msg(string)
class CmdPs(MuxCommand):
"""
@ps - list processes
Usage
@ps
Shows the process/event table.
"""
key = "@ps"
permissions = "cmd:ps"
help_category = "System"
def func(self):
"run the function."
string = "Processes Scheduled:\n-- PID [time/interval] [repeats] description --"
all_scripts = ScriptDB.objects.get_all_scripts()
repeat_scripts = [script for script in all_scripts if script.interval]
nrepeat_scripts = [script for script in all_scripts if script not in repeat_scripts]
string = "\nNon-timed scripts:"
for script in nrepeat_scripts:
string += "\n %i %s %s" % (script.id, script.key, script.desc)
string += "\n\nTimed scripts:"
for script in repeat_scripts:
repeats = "[inf] "
if script.repeats:
repeats = "[%i] " % script.repeats
string += "\n %i %s [%d/%d] %s%s" % (script.id, script.key,
script.time_until_next_repeat(),
script.interval,
repeats,
script.desc)
string += "\nTotals: %d interval scripts" % len(all_scripts)
self.caller.msg(string)
class CmdStats(MuxCommand):
"""
@stats - show object stats
Usage:
@stats
Shows stats about the database.
"""
key = "@stats"
aliases = "@db"
permissions = "cmd:stats"
help_category = "System"
def func(self):
"Show all stats"
# get counts for all typeclasses
stats_dict = ObjectDB.objects.object_totals()
# get all objects
stats_allobj = ObjectDB.objects.all().count()
# get all rooms
stats_room = ObjectDB.objects.filter(obj_location=None).count()
# get all players
stats_users = User.objects.all().count()
string = "-"*60
string += "\n Number of users: %i" % stats_users
string += "\n Total number of objects: %i" % stats_allobj
string += "\n Number of rooms (location==None): %i" % stats_room
string += "\n Object type statistics:"
for path, num in stats_dict.items():
string += "\n %i - %s" % (num, path)
self.caller.msg(string)

View file

@ -0,0 +1,147 @@
"""
The command template for the default MUX-style command set
"""
from src.utils import utils
from game.gamesrc.commands.basecommand import Command
class MuxCommand(Command):
"""
This sets up the basis for a MUX command. The idea
is that most other Mux-related commands should just
inherit from this and don't have to implement much
parsing of their own unless they do something particularly
advanced.
Note that the class's __doc__ string (this text) is
used by Evennia to create the automatic help entry for
the command, so make sure to document consistently here.
"""
def has_perm(self, srcobj):
"""
This is called by the cmdhandler to determine
if srcobj is allowed to execute this command.
We just show it here for completeness - we
are satisfied using the default check in Command.
"""
return super(MuxCommand, self).has_perm(srcobj)
def parse(self):
"""
This method is called by the cmdhandler once the command name
has been identified. It creates a new set of member variables
that can be later accessed from self.func() (see below)
The following variables are available for our use when entering this
method (from the command definition, and assigned on the fly by the
cmdhandler):
self.key - the name of this command ('look')
self.aliases - the aliases of this cmd ('l')
self.permissions - permission string for this command
self.help_category - overall category of command
self.caller - the object calling this command
self.cmdstring - the actual command name used to call this
(this allows you to know which alias was used,
for example)
self.args - the raw input; everything following self.cmdstring.
self.cmdset - the cmdset from which this command was picked. Not
often used (useful for commands like 'help' or to
list all available commands etc)
self.obj - the object on which this command was defined. It is often
the same as self.caller.
A MUX command has the following possible syntax:
name[ with several words][/switch[/switch..]] arg1[,arg2,...] [[=|,] arg[,..]]
The 'name[ with several words]' part is already dealt with by the
cmdhandler at this point, and stored in self.cmdname (we don't use
it here). The rest of the command is stored in self.args, which can start
with the switch indicator /.
This parser breaks self.args into its constituents and stores them in the
following variables:
self.switches = [list of /switches (without the /)]
self.raw = This is the raw argument input, including switches
self.args = This is re-defined to be everything *except* the switches
self.lhs = Everything to the left of = (lhs:'left-hand side'). If
no = is found, this is identical to self.args.
self.rhs: Everything to the right of = (rhs:'right-hand side').
If no '=' is found, this is None.
self.lhslist - [self.lhs split into a list by comma]
self.rhslist - [list of self.rhs split into a list by comma]
self.arglist = [list of space-separated args (stripped, including '=' if it exists)]
All args and list members are stripped of excess whitespace around the
strings, but case is preserved.
"""
raw = self.args
args = raw.strip()
# split out switches
switches = []
if args and len(args) >1 and args[0] == "/":
# we have a switch, or a set of switches. These end with a space.
#print "'%s'" % args
switches = args[1:].split(None, 1)
if len(switches) > 1:
switches, args = switches
switches = switches.split('/')
else:
args = ""
switches = switches[0].split('/')
arglist = [arg.strip() for arg in args.split(None)]
# check for arg1, arg2, ... = argA, argB, ... constructs
lhs, rhs = args, None
lhslist, rhslist = [arg.strip() for arg in args.split(',')], []
if args and '=' in args:
lhs, rhs = [arg.strip() for arg in args.split('=', 1)]
lhslist = [arg.strip() for arg in lhs.split(',')]
rhslist = [arg.strip() for arg in rhs.split(',')]
# save to object properties:
self.raw = raw
self.switches = switches
self.args = args.strip()
self.arglist = arglist
self.lhs = lhs
self.lhslist = lhslist
self.rhs = rhs
self.rhslist = rhslist
def func(self):
"""
This is the hook function that actually does all the work. It is called
by the cmdhandler right after self.parser() finishes, and so has access
to all the variables defined therein.
"""
# a simple test command to show the available properties
string = "-" * 50
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "-" * 50
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
string += "cmd perms (self.permissions): {w%s{n\n" % self.permissions
string += "help category (self.help_category): {w%s{n\n" % self.help_category
string += "object calling (self.caller): {w%s{n\n" % self.caller
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
# show cmdset.key instead of cmdset to shorten output
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
string += "\n" + "-" * 50
string += "\nVariables from MuxCommand baseclass\n"
string += "-" * 50
string += "\nraw argument (self.raw): {w%s{n \n" % self.raw
string += "cmd args (self.args): {w%s{n\n" % self.args
string += "cmd switches (self.switches): {w%s{n\n" % self.switches
string += "space-separated arg list (self.arglist): {w%s{n\n" % self.arglist
string += "lhs, left-hand side of '=' (self.lhs): {w%s{n\n" % self.lhs
string += "lhs, comma separated (self.lhslist): {w%s{n\n" % self.lhslist
string += "rhs, right-hand side of '=' (self.rhs): {w%s{n\n" % self.rhs
string += "rhs, comma separated (self.rhslist): {w%s{n\n" % self.rhslist
string += "-" * 50
self.caller.msg(string)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,670 @@
"""
This file contains commands that require special permissions to
use. These are generally @-prefixed commands, but there are
exceptions.
"""
import traceback
from django.contrib.auth.models import User
from src.server import sessionhandler
from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB
from src.permissions.models import PermissionGroup
from src.scripts.scripthandler import format_script_list
from src.utils import reloads, create, logger, utils
from src.permissions.permissions import has_perm
from game.gamesrc.commands.default.muxcommand import MuxCommand
class CmdReload(MuxCommand):
"""
Reload the system
Usage:
@reload
This reloads the system modules and
re-validates all scripts.
"""
key = "@reload"
permissions = "cmd:reload"
help_category = "System"
def func(self):
"""
reload the system.
"""
caller = self.caller
reloads.reload_modules()
reloads.reload_scripts()
reloads.reload_commands()
class CmdPy(MuxCommand):
"""
Execute a snippet of python code
Usage:
@py <cmd>
In this limited python environment, only two
variables are defined: 'self'/'me' which refers to one's
own object, and 'here' which refers to the current
location.
"""
key = "@py"
aliases = ["!"]
permissions = "cmd:py"
help_category = "Admin"
def func(self):
"hook function"
caller = self.caller
pycode = self.args
if not pycode:
string = "Usage: @py <code>"
caller.msg(string)
return
# create temporary test objects for playing with
script = create.create_script("src.scripts.scripts.DoNothing",
'testscript')
obj = create.create_object("src.objects.objects.Object",
'testobject')
available_vars = {'self':caller,
'me':caller,
'here':caller.location,
'obj':obj,
'script':script}
caller.msg(">>> %s" % pycode)
try:
ret = eval(pycode, {}, available_vars)
ret = "<<< %s" % str(ret)
except Exception:
try:
exec(pycode, {}, available_vars)
ret = "<<< Done."
except Exception:
errlist = traceback.format_exc().split('\n')
if len(errlist) > 4:
errlist = errlist[4:]
ret = "\n".join("<<< %s" % line for line in errlist if line)
caller.msg(ret)
obj.delete()
script.delete()
class CmdListScripts(MuxCommand):
"""
List all scripts.
Usage:
@scripts[/switches] [<obj or scriptid>]
Switches:
stop - stops an existing script
validate - run a validation on the script(s)
If no switches are given, this command just views all active
scripts. The argument can be either an object, at which point it
will be searched for all scripts defined on it, or an script name
or dbref. For using the /stop switch, a unique script dbref is
required since whole classes of scripts often have the same name.
"""
key = "@scripts"
aliases = "@listscripts"
permissions = "cmd:listscripts"
help_category = "Admin"
def func(self):
"implement method"
caller = self.caller
args = self.args
string = ""
if args:
# test first if this is an script match
scripts = ScriptDB.objects.get_all_scripts(key=args)
if not scripts:
# try to find an object instead.
objects = ObjectDB.objects.pobject_search(caller,
args,
global_search=True)
if objects:
scripts = []
for obj in objects:
# get all scripts on the object(s)
scripts.extend(ScriptDB.objects.get_all_scripts_on_obj(obj))
else:
# we want all scripts.
scripts = ScriptDB.objects.get_all_scripts()
if not scripts:
return
#caller.msg(scripts)
if self.switches and self.switches[0] in ('stop', 'del', 'delete'):
# we want to delete something
if not scripts:
string = "No scripts/objects matching '%s'. " % args
string += "Be more specific."
elif len(scripts) == 1:
# we have a unique match!
string = "Stopping script '%s'." % scripts[0].key
scripts[0].stop()
ScriptDB.objects.validate() #just to be sure all is synced
else:
# multiple matches.
string = "Multiple script matches. Please refine your search:\n"
string += ", ".join([str(script.key) for script in scripts])
elif self.switches and self.switches[0] in ("validate", "valid", "val"):
# run validation on all found scripts
nr_started, nr_stopped = ScriptDB.objects.validate(scripts=scripts)
string = "Validated %s scripts. " % ScriptDB.objects.all().count()
string += "Started %s and stopped %s scripts." % (nr_started,
nr_stopped)
else:
# No stopping or validation. We just want to view things.
string = format_script_list(scripts)
caller.msg(string)
class CmdListCmdSets(MuxCommand):
"""
list command sets on an object
Usage:
@listcmdsets [obj]
This displays all cmdsets assigned
to a user. Defaults to yourself.
"""
key = "@listcmdsets"
permissions = "cmd:listcmdsets"
def func(self):
"list the cmdsets"
caller = self.caller
if self.arglist:
obj = caller.search(self.arglist[0])
if not obj:
return
else:
obj = caller
string = "%s" % obj.cmdset
caller.msg(string)
class CmdListObjects(MuxCommand):
"""
List all objects in database
Usage:
@listobjects [nr]
Gives a list of nr latest objects in database ang give
statistics. If not given, nr defaults to 10.
"""
key = "@listobjects"
aliases = ["@listobj", "@listobjs"]
permissions = "cmd:listobjects"
help_category = "Building"
def func(self):
"Implement the command"
caller = self.caller
if self.args and self.args.isdigit():
nlim = int(self.args)
else:
nlim = 10
dbtotals = ObjectDB.objects.object_totals()
#print dbtotals
string = "\nObjects in database:\n"
string += "Count\tTypeclass"
for path, count in dbtotals.items():
string += "\n %s\t%s" % (count, path)
string += "\nLast %s Objects created:" % nlim
objs = list(ObjectDB.objects.all())
for i, obj in enumerate(objs):
if i <= nlim:
string += "\n %s\t%s(#%i) (%s)" % \
(obj.date_created, obj.name, obj.id, str(obj.typeclass))
else:
break
caller.msg(string)
class CmdBoot(MuxCommand):
"""
@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.
"""
key = "@boot"
permissions = "cmd:boot"
help_category = "Admin"
def func(self):
"Implementing the function"
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @boot[/switches] <player> [:reason]")
return
if ':' in args:
args, reason = [a.strip() for a in args.split(':', 1)]
boot_list = []
reason = ""
if 'port' in self.switches:
# Boot a particular port.
sessions = sessionhandler.get_session_list(True)
for sess in sessions:
# Find the session with the matching port number.
if sess.getClientAddress()[1] == int(args):
boot_list.append(sess)
break
else:
# Boot by player object
pobj = caller.search("*%s" % args, global_search=True)
if not pobj:
return
pobj = pobj[0]
if pobj.has_player:
if not has_perm(caller, pobj, 'can_boot'):
string = "You don't have the permission to boot %s."
pobj.msg(string)
return
# we have a bootable object with a connected user
matches = sessionhandler.sessions_from_object(pobj)
for match in matches:
boot_list.append(match)
else:
caller.msg("That object has no connected player.")
return
if not boot_list:
caller.msg("No matches found.")
return
# Carry out the booting of the sessions in the boot list.
feedback = None
if not 'quiet' in self.switches:
feedback = "You have been disconnected by %s.\n" % caller.name
if reason:
feedback += "\nReason given: %s" % reason
for session in boot_list:
name = session.name
if feedback:
session.msg(feedback)
session.disconnectClient()
sessionhandler.remove_session(session)
caller.msg("You booted %s." % name)
class CmdDelPlayer(MuxCommand):
"""
delplayer - delete player from server
Usage:
@delplayer[/switch] <name> [: reason]
Switch:
delobj - also delete the player's currently
assigned in-game object.
Completely deletes a user from the server database,
making their nick and e-mail again available.
"""
key = "@delplayer"
permissions = "cmd:delplayer"
help_category = "Admin"
def func(self):
"Implements the command."
caller = self.caller
args = self.args
if not args:
caller.msg("Usage: @delplayer[/delobj] <player/user name or #id>")
return
reason = ""
if ':' in args:
args, reason = [arg.strip() for arg in args.split(':', 1)]
# Search for the object connected to this user (this is done by
# adding a * to the beginning of the search criterion)
pobj = caller.search("*%s" % args, global_search=True)
if not pobj:
# if we cannot find an object connected to this user,
# try a more direct approach
try:
user = User.objects.get(id=args)
except Exception:
try:
user = User.objects.get(name__iexact=args)
except Exception:
caller.msg("Could not find user/id '%s'." % args)
return
uprofile = user.get_profile
else:
user = pobj.user
uprofile = pobj.user_profile
if not has_perm(caller, uprofile, 'manage_players'):
string = "You don't have the permissions to delete that player."
caller.msg(string)
return
uname = user.username
# boot the player then delete
if pobj and pobj.has_user:
caller.msg("Booting and informing player ...")
msg = "\nYour account '%s' is being *permanently* deleted.\n" % uname
if reason:
msg += " Reason given:\n '%s'" % reason
pobj.msg(msg)
caller.execute_cmd("@boot %s" % uname)
uprofile.delete()
user.delete()
caller.msg("Player %s was successfully deleted." % uname)
class CmdNewPassword(MuxCommand):
"""
@newpassword
Usage:
@newpassword <user obj> = <new password>
Set a player's password.
"""
key = "@newpassword"
permissions = "cmd:newpassword"
help_category = "Admin"
def func(self):
"Implement the function."
caller = self.caller
if not self.rhs:
caller.msg("Usage: @newpassword <user obj> = <new password>")
return
pobj = caller.search("*%s" % self.lhs, global_search=True)
if not pobj:
# if we cannot find an object connected to this user,
# try a more direct approach
try:
user = User.objects.get(id=self.lhs)
except Exception:
try:
user = User.objects.get(name__iexact=self.lhs)
except Exception:
caller.msg("Could not find user/id '%s'." % self.lhs)
return
else:
user = pobj.user
user.set_password(self.rhs)
user.save()
caller.msg("%s - new password set to '%s'." % (user.username, self.rhs))
if pobj:
pobj.msg("%s has changed your password to '%s'." % (caller.name, self.rhs))
class CmdHome(MuxCommand):
"""
home
Usage:
home
Teleport the player to their home.
"""
key = "home"
permissions = "cmd:home"
def func(self):
"Implement the command"
caller = self.caller
home = caller.home
if not home:
caller.msg("You have no home set.")
else:
caller.move_to(home)
caller.msg("There's no place like home ...")
class CmdService(MuxCommand):
"""
@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.
"""
key = "@service"
permissions = "cmd:service"
help_category = "Admin"
def func(self):
"Implement command"
caller = self.caller
switches = self.switches
if not switches or \
switches[0] not in ["list","start","stop"]:
caller.msg("Usage: @servive/<start|stop|list> [service]")
return
switch = switches[0]
# get all services
sessions = caller.sessions
if not sessions:
return
service_collection = sessions[0].server.service_collection
if switch == "list":
# Just display the list of installed services and their
# status, then exit.
string = "-" * 40
string += "\nService Listing"
for service in service_collection.services:
if service.running:
status = 'Running'
else:
status = 'Inactive'
string += '\n * %s (%s)' % (service.name, status)
string += "\n" + "-" * 40
caller.msg(string)
return
# Get the service to start / stop
try:
service = service_collection.getServiceNamed(self.args)
except Exception:
string = 'Invalid service name. This command is case-sensitive. '
string += 'See @service/list.'
caller.msg(string)
return
if switch == "stop":
# Stopping a service gracefully closes it and disconnects
# any connections (if applicable).
if not service.running:
caller.msg('That service is not currently running.')
return
# We don't want to kill the 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':
string = "You can not stop Evennia TCPServer services this way."
string += "\nTo e.g. remove a listening port, change settings file and restart."
caller.msg(string)
return
#comsys.cemit_mudinfo("%s is *Stopping* the service '%s'." % (sname, service.name)) #TODO!
service.stopService()
return
if switch == "start":
#Starts a service.
if service.running:
caller.msg('That service is already running.')
return
#comsys.cemit_mudinfo("%s is *Starting* the service '%s'." % (sname,service.name)) #TODO!
service.startService()
class CmdShutdown(MuxCommand):
"""
@shutdown
Usage:
@shutdown [announcement]
Shut the game server down gracefully.
"""
key = "@shutdown"
permissions = "cmd:shutdown"
help_category = "System"
def func(self):
"Define function"
try:
session = self.caller.sessions[0]
except Exception:
return
self.caller.msg('Shutting down server ...')
announcement = "\nServer is being SHUT DOWN!\n"
if self.args:
announcement += "%s\n" % self.args
sessionhandler.announce_all(announcement)
logger.log_infomsg('Server shutdown by %s.' % self.caller.name)
# access server through session so we don't call server directly
# (importing it directly would restart it...)
session.server.shutdown()
class CmdPerm(MuxCommand):
"""
@perm - set permissions
Usage:
@perm[/switch] [<user>] = [<permission>]
Switches:
del : delete the given permission from <user>.
list : list all permissions, or those set on <user>
This command sets/clears individual permission strings on a user.
Use /list without any arguments to see all available permissions
or those defined on the <user> argument.
"""
key = "@perm"
permissions = "cmd:perm"
help_category = "Admin"
def func(self):
"Implement function"
caller = self.caller
switches = self.switches
lhs, rhs = self.lhs, self.rhs
if not self.args and "list" not in switches:
caller.msg("Usage: @setperm[/switch] [user = permission]")
return
if "list" in switches:
#just print all available permissions
string = "\nAll currently available permissions (i.e. not locks):"
pgroups = PermissionGroup.objects.all()
for pgroup in pgroups:
string += "\n\n - %s (%s):" % (pgroup.key, pgroup.desc)
string += "\n%s" % \
utils.fill(", ".join(pgroup.group_permissions.split(',')))
caller.msg(string)
return
pobj = caller.search("*%s" % self.lhs, global_search=True)
if not pobj:
# if we cannot find an object connected to this user,
# try a more direct approach
try:
user = User.objects.get(id=self.lhs)
except Exception:
try:
user = User.objects.get(name__iexact=self.lhs)
except Exception:
caller.msg("Could not find user/id '%s'." % self.lhs)
return
else:
pobj = pobj
user = pobj.user
uprofile = user.get_profile()
if not rhs:
#if we didn't have any =, we list the permissions set on <object>.
if user.is_superuser:
string = "\n This is a SUPERUSER account! "
string += "All permissions are automatically set."
else:
string = "Permissions set on this object:\n"
string += uprofile.permissions
caller.msg(string)
return
# we supplied an argument on the form obj = perm
if 'del' in switches:
# delete the given permission from object.
uprofile.del_perm(rhs)
caller.msg("Permission '%s' removed (if it existed)." % rhs)
if pobj:
pobj.msg("%s revokes the permission '%s' from you." % (caller.name, rhs))
else:
# As an extra check, we warn the user if they customize the
# permission string (which is okay, and is used by the lock system)
uprofile.set_perm(rhs)
string = "Permission '%s' given to %s." % (rhs, uprofile.user.username)
if not any(group.contains(rhs)
for group in PermissionGroup.objects.all()):
string += "Note: The given permission is not found in any permission groups."
string += "\nThis is not an error, it just shows that it will work only as a lock."
caller.msg(string)
if pobj:
pobj.msg("%s granted you the permission '%s'." % (caller.name, rhs))

View file

@ -0,0 +1,173 @@
"""
System commands
These are the default commands called by the system commandhandler
when various exceptions occur. If one of these commands are not
implemented and part of the current cmdset, the engine falls back
to a default solution instead.
Some system commands are shown in this module
as a REFERENCE only (they are not all added to Evennia's
default cmdset since they don't currently do anything differently from the
default backup systems hard-wired in the engine).
Overloading these commands in a cmdset can be used to create
interesting effects. An example is using the NoMatch system command
to implement a line-editor where you don't have to start each
line with a command (if there is no match to a known command,
the line is just added to the editor buffer).
"""
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.comms.models import Channel
from src.utils import create
from src.permissions.permissions import has_perm
# The command keys the engine is calling
# (the actual names all start with __)
from src.commands.cmdhandler import CMD_NOINPUT
from src.commands.cmdhandler import CMD_NOMATCH
from src.commands.cmdhandler import CMD_MULTIMATCH
from src.commands.cmdhandler import CMD_NOPERM
from src.commands.cmdhandler import CMD_CHANNEL
from src.commands.cmdhandler import CMD_EXIT
# Command called when there is no input at line
# (i.e. an lone return key)
class SystemNoInput(MuxCommand):
"""
This is called when there is no input given
"""
key = CMD_NOINPUT
def func(self):
"Do nothing."
pass
#
# Command called when there was no match to the
# command name
#
class SystemNoMatch(MuxCommand):
"""
No command was found matching the given input.
"""
key = CMD_NOMATCH
def func(self):
"""
This is given the failed raw string as input.
"""
self.caller.msg("Huh?")
#
# Command called when there were mulitple matches to the command.
#
class SystemMultimatch(MuxCommand):
"""
Multiple command matches
"""
key = CMD_MULTIMATCH
def func(self):
"""
argument to cmd is a comma-separated string of
all the clashing matches.
"""
self.caller.msg("Multiple matches found:\n %s" % self.args)
class SystemNoPerm(MuxCommand):
"""
This is called when the user does not have the
correct permissions to use a particular command.
"""
key = CMD_NOPERM
def func(self):
"""
This receives the original raw
input string (the one whose command failed to validate)
as argument.
"""
self.caller.msg("You are not allowed to do that.")
# Command called when the comman given at the command line
# was identified as a channel name, like there existing a
# channel named 'ooc' and the user wrote
# > ooc Hello!
class SystemSendToChannel(MuxCommand):
"""
This is a special command that the cmdhandler calls
when it detects that the command given matches
an existing Channel object key (or alias).
"""
key = CMD_CHANNEL
permissions = "cmd:use_channels"
def parse(self):
channelname, msg = self.args.split(':', 1)
self.args = channelname.strip(), msg.strip()
def func(self):
"""
Create a new message and send it to channel, using
the already formatted input.
"""
caller = self.caller
channelkey, msg = self.args
if not msg:
caller.msg("Say what?")
return
channel = Channel.objects.get_channel(channelkey)
if not channel:
caller.msg("Channel '%s' not found." % channelkey)
return
if not channel.has_connection(caller):
string = "You are not connected to channel '%s'."
caller.msg(string % channelkey)
return
if not has_perm(caller, channel, 'chan_send'):
string = "You are not permitted to send to channel '%s'."
caller.msg(string % channelkey)
return
msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
msgobj = create.create_message(caller, msg, channels=[channel])
channel.msg(msgobj)
#
# Command called when the system recognizes the command given
# as matching an exit from the room. E.g. if there is an exit called 'door'
# and the user gives the command
# > door
# the exit 'door' should be traversed to its destination.
class SystemUseExit(MuxCommand):
"""
Handles what happens when user gives a valid exit
as a command. It receives the raw string as input.
"""
key = CMD_EXIT
def func(self):
"""
Handle traversing an exit
"""
caller = self.caller
if not self.args:
return
exit_name = self.args
exi = caller.search(exit_name)
if not exi:
return
destination = exi.attr('_destination')
if not destination:
return
if has_perm(caller, exit, 'traverse'):
caller.move_to(destination)
else:
caller.msg("You cannot enter")

View file

@ -0,0 +1,190 @@
"""
This defines some test commands for use while testing the MUD.
Just remove these commands from the default state when they
are not needed anymore.
"""
from django.db import IntegrityError
from src.comms.models import Msg
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.permissions import permissions
from src.utils import create
# Test permissions
class CmdTest(MuxCommand):
"""
test the command system
Usage:
@test <any argument or switch>
This command will echo back all argument or switches
given to it, showcasing the muxcommand style.
"""
key = "@test"
aliases = ["@te", "@test all"]
permissions = "cmd:Immortals Wizards"
# the muxcommand class itself handles the display
# so we just defer to it by not adding any function.
pass
class CmdTestPerms(MuxCommand):
"""
Test command - test permissions
Usage:
@testperm [[lockstring] [=permstring]]
With no arguments, runs a sequence of tests for the
permission system using the calling player's permissions.
If <lockstring> is given, match caller's permissions
against these locks. If also <permstring> is given,
match this against the given locks instead.
"""
key = "@testperm"
permissions = "cmd:Immortals Wizards"
def func(self, srcobj, inp):
"""
Run tests
"""
if srcobj.user.is_superuser:
srcobj.msg("You are a superuser. Permission tests are pointless.")
return
# create a test object
obj = create.create_object(None, "accessed_object") # this will use default typeclass
obj_id = obj.id
srcobj.msg("obj_attr: %s" % obj.attr("testattr"))
# perms = ["has_permission", "has permission", "skey:has_permission",
# "has_id(%s)" % obj_id, "has_attr(testattr)",
# "has_attr(testattr, testattr_value)"]
# test setting permissions
uprofile = srcobj.user.get_profile()
# do testing
srcobj.msg("----------------")
permissions.set_perm(obj, "has_permission")
permissions.add_perm(obj, "skey:has_permission")
srcobj.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
srcobj.msg("normal permtest: %s" % permissions.has_perm(uprofile, obj))
srcobj.msg("skey permtest: %s" % permissions.has_perm(uprofile, obj, 'skey'))
permissions.set_perm(uprofile, "has_permission")
srcobj.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
srcobj.msg("normal permtest: %s" % permissions.has_perm(uprofile, obj))
srcobj.msg("skey permtest: %s" % permissions.has_perm(uprofile, obj, 'skey'))
# function tests
permissions.set_perm(obj, "has_id(%s)" % (uprofile.id))
srcobj.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
srcobj.msg("functest: %s" % permissions.has_perm(uprofile, obj))
uprofile.attr("testattr", "testattr_value")
permissions.set_perm(obj, "has_attr(testattr, testattr_value)")
srcobj.msg(" keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
srcobj.msg("functest: %s" % permissions.has_perm(uprofile, obj))
# cleanup of test permissions
permissions.del_perm(uprofile, "has_permission")
srcobj.msg(" cleanup: keys:[%s] locks:[%s]" % (uprofile.permissions, obj.permissions))
obj.delete()
uprofile.attr("testattr", delete=True)
# Add/remove states
EXAMPLE_STATE="game.gamesrc.commands.examples.example.EXAMPLESTATE"
class CmdTestState(MuxCommand):
"""
Test command - add a state.
Usage:
@teststate[/switch] [<python path to state instance>]
Switches:
add - add a state
clear - remove all added states.
list - view current state stack
reload - reload current state stack
If no python path is given, an example state will be added.
You will know it worked if you can use the commands '@testcommand'
and 'smile'.
"""
key = "@teststate"
alias = "@testingstate"
permissions = "cmd:Immortals Wizards"
def func(self, source_object, inp):
"""
inp is the dict returned from MuxCommand's parser.
"""
switches = inp["switches"]
if not switches or switches[0] not in ["add", "clear", "list", "reload"]:
string = "Usage: @teststate[/add|clear|list|reload] [<python path>]"
source_object.msg(string)
elif "clear" in switches:
source_object.statehandler.clear()
source_object.msg("All states cleared.")
return
elif "list" in switches:
string = "%s" % source_object.statehandler
source_object.msg(string)
elif "reload" in switches:
source_object.statehandler.load()
source_object.msg("States reloaded.")
else: #add
arg = inp["raw"]
if not arg:
arg = EXAMPLE_STATE
source_object.statehandler.add(arg)
string = "Added state '%s'." % source_object.statehandler.state.key
source_object.msg(string)
class TestCom(MuxCommand):
"""
Test the command system
Usage:
@testcom/create/list [channel]
"""
key = "@testcom"
permissions = "cmd:Immortals Wizards"
def func(self):
"Run the test program"
caller = self.caller
if 'create' in self.switches:
if self.args:
chankey = self.args
try:
channel = create.create_channel(chankey)
except IntegrityError:
caller.msg("Channel '%s' already exists." % chankey)
return
channel.connect_to(caller)
caller.msg("Created new channel %s" % chankey)
msgobj = create.create_message(caller.player,
"First post to new channel!")
channel.msg(msgobj)
return
elif 'list' in self.switches:
msgresults = Msg.objects.get_messages_by_sender(caller)
string = "\n".join(["%s %s: %s" % (msg.date_sent,
[str(chan.key) for chan in msg.channels.all()],
msg.message)
for msg in msgresults])
caller.msg(string)
return
caller.msg("Usage: @testcom/create channel")

View file

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

View file

@ -0,0 +1,130 @@
"""
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")

View file

@ -0,0 +1,235 @@
"""
Implementation of the @search command that resembles MUX2.
"""
from django.db.models import Q
#from src.objects.models import Object
from src.utils import OBJECT as 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.msg("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.msg("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 = []
# this bits gotta get totally redone
for obj in search_query:
thing_list.append(obj)
# Render each section for different object types
if thing_list:
source_object.msg("\n\rTHINGS:")
for thing in thing_list:
source_object.msg(thing.name)
if exit_list:
source_object.msg("\n\rEXITS:")
for exit in exit_list:
source_object.msg(exit.name)
if room_list:
source_object.msg("\n\rROOMS:")
for room in room_list:
source_object.msg(room.name)
if player_list:
source_object.msg("\n\rPLAYER:")
for player in player_list:
source_object.msg(player.name)
# Show the total counts by type
source_object.msg("\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.msg("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.msg("To be implemented...")
return None
elif search_type == "flags":
flag_list = search_restriction.split()
#source_object.msg("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.msg("@search class=restriction")
#source_object.msg("eq_split: %s" % eq_split)
#source_object.msg("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.msg("@search player")
#source_object.msg(first_check_split)
search_player = first_check_split[0]
else:
#source_object.msg("@search player class=restriction")
#source_object.msg(first_check_split)
search_player = first_check_split[0]
eq_split = first_check_split[1].split('=', 1)
search_type = eq_split[0]
#source_object.msg("eq_split: %s" % eq_split)
restriction_split = eq_split[1].split(',')
search_restriction = restriction_split[0]
#source_object.msg("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.msg("search_player: %s" % search_player)
#source_object.msg("search_type: %s" % search_type)
#source_object.msg("search_restriction: %s" % search_restriction)
#source_object.msg("search_lowdb: %s" % search_low_dbnum)
#source_object.msg("search_highdb: %s" % search_high_dbnum)
# Clean up these variables for comparisons.
try:
search_type = search_type.strip().lower()
except AttributeError:
pass
try:
search_restriction = search_restriction.strip().lower()
except AttributeError:
pass
# Build the search query.
search_query = build_query(source_object, search_query, search_player, search_type,
search_restriction, search_low_dbnum,
search_high_dbnum)
# Something bad happened in query construction, die here.
if search_query is None:
return
display_results(source_object, search_query)
GLOBAL_CMD_TABLE.add_command("@search", cmd_search,
priv_tuple=("objects.info",),
help_category="Building")

View file

@ -0,0 +1,295 @@
"""
Commands that are available from the connect screen.
"""
import traceback
#from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.auth.models import User
from src.players.models import PlayerDB
from src.objects.models import ObjectDB
from src.config.models import ConfigValue
from src.comms.models import Channel
from src.utils import create, logger, utils
from game.gamesrc.commands.default.muxcommand import MuxCommand
class CmdConnect(MuxCommand):
"""
Connect to the game.
Usage (at login screen):
connect <email> <password>
Use the create command to first create an account before logging in.
"""
key = "connect"
aliases = ["conn", "con", "co"]
def func(self):
"""
Uses the Django admin api. Note that unlogged-in commands
have a unique position in that their func() receives
a session object instead of a source_object like all
other types of logged-in commands (this is because
there is no object yet before the player has logged in)
"""
session = self.caller
arglist = self.arglist
if not arglist or len(arglist) < 2:
session.msg("\n\r Usage (without <>): connect <email> <password>")
return
uemail = arglist[0]
password = arglist[1]
# Match an email address to an account.
email_match = PlayerDB.objects.get_player_from_email(uemail)
# No playername match
if not email_match:
string = "The email '%s' does not match any accounts." % uemail
string += "\n\r\n\rIf you are new you should first create a new account "
string += "using the 'create' command."
session.msg(string)
return
# We have at least one result, so we can check the password.
player = email_match
if not player.user.check_password(password):
session.msg("Incorrect password.")
return
# We are logging in, get/setup the player object controlled by player
character = player.character
if not character:
# Create a new character object to tie the player to. This should
# usually not be needed unless the old character object was manually
# deleted.
default_home_id = ConfigValue.objects.conf(db_key="default_home")
default_home = ObjectDB.objects.get_id(default_home_id)
typeclass = settings.BASE_CHARACTER_TYPECLASS
character = create.create_object(typeclass=typeclass,
key=player.name,
location=default_home,
home=default_home,
player=player)
character.db.FIRST_LOGIN = "True"
# Getting ready to log the player in.
# Check if this is the first time the
# *player* connects
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
# check if this is the first time the *character*
# character (needs not be the first time the player
# does so, e.g. if the player has several characters)
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# actually do the login, calling
# customization hooks before and after.
player.at_pre_login()
character.at_pre_login()
session.login(player)
player.at_post_login()
character.at_post_login()
# run look
#print "character:", character, character.scripts.all(), character.cmdset.current
character.execute_cmd('look')
class CmdCreate(MuxCommand):
"""
Create a new account.
Usage (at login screen):
create \"playername\" <email> <password>
This creates a new player account.
"""
key = "create"
aliases = ["cre", "cr"]
def parse(self):
"""
The parser must handle the multiple-word player
name enclosed in quotes:
connect "Long name with many words" my@myserv.com mypassw
"""
super(CmdCreate, self).parse()
self.playerinfo = []
if len(self.arglist) < 3:
return
if len(self.arglist) > 3:
# this means we have a multi_word playername. pop from the back.
password = self.arglist.pop()
email = self.arglist.pop()
# what remains is the playername.
playername = " ".join(self.arglist)
else:
playername, email, password = self.arglist
playername = playername.replace('"', '') # remove "
playername = playername.replace("'", "")
self.playerinfo = (playername, email, password)
def func(self):
"Do checks and create account"
session = self.caller
try:
playername, email, password = self.playerinfo
except ValueError:
string = "\n\r Usage (without <>): create \"<playername>\" <email> <password>"
session.msg(string)
return
if not playername:
# entered an empty string
session.msg("\n\r You have to supply a longer playername, surrounded by quotes.")
return
if not email or not password:
session.msg("\n\r You have to supply an e-mail address followed by a password." )
return
if not utils.validate_email_address(email):
# check so the email at least looks ok.
session.msg("'%s' is not a valid e-mail address." % email)
return
# Run sanity and security checks
if PlayerDB.objects.get_player_from_name(playername) or User.objects.filter(username=playername):
# player already exists
session.msg("Sorry, there is already a player with the name '%s'." % playername)
elif PlayerDB.objects.get_player_from_email(email):
# email already set on a player
session.msg("Sorry, there is already a player with that email address.")
elif len(password) < 3:
# too short password
string = "Your password must be at least 3 characters or longer."
string += "\n\rFor best security, make it at least 8 characters long, "
string += "avoid making it a real word and mix numbers into it."
session.msg(string)
else:
# everything's ok. Create the new player account
try:
default_home_id = ConfigValue.objects.conf(db_key="default_home")
default_home = ObjectDB.objects.get_id(default_home_id)
typeclass = settings.BASE_CHARACTER_TYPECLASS
permissions = settings.PERMISSION_PLAYER_DEFAULT
new_character = create.create_player(playername, email, password,
permissions=permissions,
location=default_home,
typeclass=typeclass,
home=default_home)
new_character.db.FIRST_LOGIN = True
new_player = new_character.player
new_player.db.FIRST_LOGIN = True
# join the new player to the public channel
pchanneldef = settings.CHANNEL_PUBLIC
if pchanneldef:
pchannel = Channel.objects.get_channel(pchanneldef[0])
if not pchannel.connect_to(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_errmsg(string)
string = "A new account '%s' was created with the email address %s. Welcome!"
string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (playername, email, email))
except Exception:
# we have to handle traceback ourselves at this point, if
# we don't, errors will give no feedback.
string = "%s\nThis is a bug. Please e-mail an admin if the problem persists."
session.msg(string % (traceback.format_exc()))
logger.log_errmsg(traceback.format_exc())
class CmdQuit(MuxCommand):
"""
We maintain a different version of the quit command
here for unconnected players for the sake of simplicity. The logged in
version is a bit more complicated.
"""
key = "quit"
aliases = ["q", "qu"]
def func(self):
"Simply close the connection."
session = self.caller
session.msg("Good bye! Disconnecting ...")
session.handle_close()
class CmdUnconnectedLook(MuxCommand):
"""
This is an unconnected version of the look command for simplicity.
All it does is re-show the connect screen.
"""
key = "look"
aliases = "l"
def func(self):
"Show the connect screen."
try:
self.caller.game_connect_screen()
except Exception:
self.caller.msg("Connect screen not found. Enter 'help' for aid.")
class CmdUnconnectedHelp(MuxCommand):
"""
This is an unconnected version of the help command,
for simplicity. It shows a pane or info.
"""
key = "help"
aliases = ["h", "?"]
def func(self):
"Shows help"
string = \
"""Welcome to Evennia!
Commands available at this point:
create - create a new account
connect - login with an existing account
look - re-show the connect screen
help - this help
quit - leave
To login to the system, you need to do one of the following:
1) If you have no previous account, you need to use the 'create'
command followed by your desired character name (in quotes), your
e-mail address and finally a password of your choice. Like
this:
> create "Anna the Barbarian" anna@myemail.com tuK3221mP
It's always a good idea (not only here, but everywhere on the net)
to not use a regular word for your password. Make it longer than
3 characters (ideally 6 or more) and mix numbers and capitalization
into it. Now proceed to 2).
2) If you have an account already, either because you just created
one in 1) above, or you are returning, use the 'connect' command
followed by the e-mail and password you previously set.
Example:
> connect anna@myemail.com tuK3221mP
This should log you in. Run 'help' again once you're logged in
to get more aid. Welcome to Evennia!
You can use the 'look' command if you want to see the connect screen again.
"""
self.caller.msg(string)

View file

@ -0,0 +1,292 @@
"""
This defines the cmdset for the red_button. Here we have defined
the commands and the cmdset in the same module, but if you
have many different commands to merge it if often better
to define the cmdset separately, picking and choosing from
among the available commands as to what should be included in the
cmdset - this way you can often re-use the commands too.
"""
import random
from src.commands.cmdset import CmdSet
from game.gamesrc.commands.basecommand import Command
# Some simple commands for the red button
#------------------------------------------------------------
# Commands defined for the red button
#------------------------------------------------------------
class CmdNudge(Command):
"""
Try to nudge the button's lid
Usage:
nudge lid
This command will have you try to
push the lid of the button away.
"""
key = "nudge lid" # two-word command name!
alias = ["nudge"]
def func(self):
"""
nudge the lid.
"""
rand = random.random()
if rand < 0.5:
string = "You nudge at the lid. It seems stuck."
elif 0.5 <= 0.5 < 0.7:
string = "You move the lid back and forth. It won't budge."
else:
string = "You manage to get a nail under the lid. It pops open."
self.obj.open_lid()
self.caller.msg(string)
class CmdPush(Command):
"""
Push the red button
Usage:
push button
"""
key = "push button"
aliases = ["push", "press button", "press"]
def func(self):
"""
Note that we choose to implement this with checking for
if the lid is open/closed. This is because this command
is likely to be tries regardless of the state of the lid.
An alternative would be to make two versions of this command
and tuck them into the cmdset linked to the Open and Closed
lid-state respectively.
"""
if self.obj.db.lid_open:
string = "You reach out to press the big red button ..."
string += "\n\nA BOOM! A bright light blinds you!"
string += "\nThe world goes dark ..."
self.caller.msg(string)
self.obj.press_button(self.caller)
self.caller.location.msg_contents("%s presses the button. BOOM! %s is blinded by a flash!" %
(self.caller.name, self.caller.name), exclude=self.caller)
else:
string = "You cannot push the button - there is a glass lid covering it."
self.caller.msg(string)
class CmdSmashGlass(Command):
"""
smash glass
Usage:
smash glass
Try to smash the glass of the button.
"""
key = "smash glass"
aliases = ["smash lid", "break lid", "smash"]
def func(self):
"""
The lid won't open, but there is a small chance
of causing the lamp to break.
"""
rand = random.random()
if rand < 0.2:
string = "You smash your hand against the glass"
string += " with all your might. The lid won't budge"
string += " but you cause quite the tremor through the button's mount."
self.caller.msg(string) # have to be called before breakage since that
# also gives a return feedback to the room.
self.obj.break_lamp()
return
elif rand < 0.6:
string = "You hit the lid hard. It doesn't move an inch."
else:
string = "You place a well-aimed fist against the glass of the lid."
string += "Unfortunately all you get is a pain in your hand. Maybe"
string += " you should just try to open the lid instead?"
self.caller.msg(string)
self.caller.location.msg_contents("%s tries to smash the glass of the button." %
(self.caller.name), exclude=self.caller)
class CmdOpenLid(Command):
"""
open lid
Usage:
open lid
"""
key = "open lid"
aliases = ["open button", 'open']
def func(self):
"simply call the right function."
if self.obj.db.lid_locked:
self.caller.msg("This lid seems locked in place for the moment.")
return
self.caller.location.msg_contents("%s opens the lid of the button." %
(self.caller.name), exclude=self.caller)
self.obj.open_lid()
class CmdCloseLid(Command):
"""
close the lid
Usage:
close lid
Closes the lid of the red button.
"""
key = "close lid"
aliases = ["close"]
def func(self):
"Close the lid"
self.obj.close_lid()
self.caller.location.msg_contents("%s closes the button's lid." %
(self.caller.name), exclude=self.caller)
class CmdBlindLook(Command):
"""
Looking around in darkness
Usage:
look <obj>
... not that there's much to see in the dark.
"""
key = "look"
aliases = ["l", "get", "examine", "ex", "feel", "listen"]
def func(self):
"This replaces all the senses when blinded."
# we decide what to reply based on which command was
# actually tried
if self.cmdstring == "get":
string = "You fumble around blindly without finding anything."
elif self.cmdstring == "examine":
string = "You try to examine your surroundings, but can't see a thing."
elif self.cmdstring == "listen":
string = "You are deafened by the boom."
elif self.cmdstring == "feel":
string = "You fumble around, hands outstretched. You bump your knee."
else:
# trying to look
string = "You are temporarily blinded by the flash. "
string += "Until it wears off, all you can do is feel around blindly."
self.caller.msg(string)
self.caller.location.msg_contents("%s stumbles around, blinded." %
(self.caller.name), exclude=self.caller)
class CmdBlindHelp(Command):
"""
Help function while in the blinded state
Usage:
help
"""
key = "help"
aliases = "h"
def func(self):
"Give a message."
self.caller.msg("You are beyond help ... until you can see again.")
#---------------------------------------------------------------
# Command sets for the red button
#---------------------------------------------------------------
# We next tuck these commands into their respective command sets.
# (note that we are overdoing the cdmset separation a bit here
# to show how it works).
class DefaultCmdSet(CmdSet):
"""
The default cmdset always sits
on the button object and whereas other
command sets may be added/merge onto it
and hide it, removing them will always
bring it back. It's added to the object
using obj.cmdset.add_default().
"""
key = "RedButtonDefault"
mergetype = "Union" # this is default, we don't really need to put it here.
def at_cmdset_creation(self):
"Init the cmdset"
self.add(CmdPush())
class LidClosedCmdSet(CmdSet):
"""
A simple cmdset tied to the redbutton object.
It contains the commands that launches the other
command sets, making the red button a self-contained
item (i.e. you don't have to manually add any
scripts etc to it when creating it).
"""
key = "LidClosedCmdSet"
# default Union is used *except* if we are adding to a
# cmdset named RedButtonOpen - this one we replace
# completely.
key_mergetype = {"LidOpenCmdSet": "Replace"}
def at_cmdset_creation(self):
"Populates the cmdset when it is instantiated."
self.add(CmdNudge())
self.add(CmdSmashGlass())
self.add(CmdOpenLid())
class LidOpenCmdSet(CmdSet):
"""
This is the opposite of the Closed cmdset.
"""
key = "LidOpenCmdSet"
# default Union is used *except* if we are adding to a
# cmdset named RedButtonClose - this one we replace
# completely.
key_mergetype = {"LidClosedCmdSet": "Replace"}
def at_cmdset_creation(self):
"setup the cmdset (just one command)"
self.add(CmdCloseLid())
class BlindCmdSet(CmdSet):
"""
This is the cmdset added to the *player* when
the button is pushed.
"""
key = "BlindCmdSet"
# we want it to completely replace all normal commands
# until the timed script removes it again.
mergetype = "Replace"
# we want to stop the player from walking around
# in this blinded state, so we hide all exits too.
# (channel commands will still work).
no_exits = True # keep player in the same room
no_objs = True # don't allow object commands
def at_cmdset_creation(self):
"Setup the blind cmdset"
self.add(CmdBlindLook())
self.add(CmdBlindHelp())

View file

@ -1,129 +0,0 @@
"""
This is an example command module for showing the pluggable command
system in action.
You'll need to make sure that this or any new modules you create are
added to game/settings.py under CUSTOM_COMMAND_MODULES or
CUSTOM_UNLOGGED_COMMAND_MODULES, which are tuples of module import
path strings. See src/config_defaults.py for more details.
E.g. to add this example command for testing, your entry in
game/settings.py would look like this:
CUSTOM_COMMAND_MODULES = ('game.gamesrc.commands.examples.example',)
(note the extra comma at the end to make this into a Python
tuple. It's only needed if you have only one entry.) You need to
restart the Evennia server before new files are recognized. Once this
is done once, you don't have to restart again, just use
@reload/commands to use the changes you make to your modules.
"""
# This is the common global CommandTable object which we'll be adding the
# example command(s) to.
from src.cmdtable import GLOBAL_CMD_TABLE
# The main command definition. We can add any number of commands this way in the
# same file.
def cmd_example(command):
"""
example - example command
Usage:
@testcommand[/switches] <text>
switches:
(can be any string, e.g. /test1 or /tom/sarah/peter)
This is the help text for the 'example' command, a command to
show how the pluggable command system works.
For testing, you can try calling this with different switches and
arguments, like
> example/test/test2 Hello
and see what is returned.
[[example_auto_help]]
This is a subtopic to the main example command help entry. It is
done by the help system splitting the text by markup of the
form [ [title ] ] (with no spaces between the square brackets)
Note that this help entry is auto-added as long as HELP_AUTO
is not set to False in your game/settings.py file.
Any number of subtopics like this one can be added on the fly
using the auto-help system. See help topics on 'help' and
'help_markup' for more information and options.
"""
# By building one big string and passing it at once, we cut down on a lot
# of emit_to() calls, which is generally a good idea.
retval = "----- Example Command -----\n\r"
# source_object is the object executing the command
retval += " Source object: %s\n\r" % command.source_object
# session points to a user Session (session.py) object (if applicable)
retval += " Session: %s\n\r" % command.session
# The raw, un-parsed input
retval += " Raw input: %s\n\r" % command.raw_input
# The command name being executed
retval += " Command: %s\n\r" % command.command_string
# A list of switches provided (if any)
retval += " Switches: %s\n\r" % command.command_switches
# A string with any arguments provided with the command
retval += " Arguments: %s\n\r" % command.command_argument
# The function that was looked up via cmdtable.py
retval += " Function: %s\n\r" % command.command_function
# Extra variables passed with cmdtable.py's add_command().
retval += " Extra vars: %s\n\r" % command.extra_vars
# Some more info for more advanced commands.
if not command.command_switches and \
command.command_argument:
retval += "\n Obs: When no switches, also multi-word\n"
retval += " command names are possible. Max allowed\n"
retval += " length is set in game/settings.py.\n"
retval += " So if there exist a matching command in the\n"
retval += " command table, Evennia would also allow\n"
retval += " the following as valid commands (and the\n"
retval += " argument list would shrink accordingly):\n"
multi = ""
for arg in command.command_argument.split():
multi += " %s" % arg
retval += " %s%s\n" % (command.command_string, multi)
# send string to player
command.source_object.emit_to(retval)
# Add the command to the common global command table. Note that
# this will auto-create help entries 'example' and
# "example_auto_help" for us.
GLOBAL_CMD_TABLE.add_command("@testcommand", cmd_example)
#
# another simple example
#
def cmd_emote_smile(command):
"""
smile - break a smile
Usage:
smile
A 'smile' emote.
"""
#get the source object (that is, the player using the command)
source_object = command.source_object
#find name of caller
name = source_object.get_name(show_dbref=False)
#get the location caller is at
location = source_object.get_location()
#build the emote
text = "%s smiles." % name
#emit the emote to everyone at the current location
location.emit_to_contents(text)
# add to global command table (we probably want an auto-help entry
# for this, but we are turning auto-help off anyway, to show
# how it works)
GLOBAL_CMD_TABLE.add_command('smile', cmd_emote_smile,
auto_help_override=False)

View file

@ -1,136 +0,0 @@
"""
This module contains various commands for testing some
of Evennia's subsystems. They were used for initial testing
but are also instructive for playing around with to learn
how different systems work. See also state_example.py.
To make these commands available in-game, add this module
to the CUSTOM_COMMAND_MODULES tuple in game/settings.py
as 'game.gamesrc.commands.examples.misc_tests'.
None of these commands are auto-added to the help database
(they have no docstrings) in order to help make it clean.
"""
from src.cmdtable import GLOBAL_CMD_TABLE
#------------------------------------------------------------
# Tests of the event system
#------------------------------------------------------------
def cmd_testevent(command):
#
# This test allows testing the event system
#
# Usage:
# @testevent [pid]
#
# Without argument, this command creates
# a dummy event in the process table.
# Use @ps to see it. Give the equivalent
# pid to remove it again (careful though,
# this command can also remove useful
# events if you give the wrong pid).
#
from src import events
from src import scheduler
source_object = command.source_object
if not source_object.is_superuser():
# To avoid accidental access to process table
source_object.emit_to("This command is superuser only.")
return
if not command.command_argument:
# No argument given; create a new test event.
event = events.IntervalEvent()
event.description = "Test event created with @testevent."
event.repeats = 3
event.interval = 5
pid = scheduler.add_event(event)
string = "Event with pid %s added. " % pid
string += "It repeats %i times and waits " % event.repeats
string += "for %i seconds between each repeat." % event.interval
string += "After all repeats, it will delete itself."
string += "\nUse @ps to see it and give this "
string += "command with the pid as argument to delete it."
source_object.emit_to(string)
else:
# An argument given; assume this is a pid.
try:
pid = int(command.command_argument)
except:
source_object.emit_to("Not a valid argument. You must give a number.")
return
if pid < 3:
string = "This low pid might belong to a system process, \n"
string += "so as a safety measure you cannot delete it using \n"
string += "this test command. Use @delevent instead."
source_object.emit_to(string)
return
pid = command.command_argument
scheduler.del_event(pid)
string = "Event with pid %s removed (if it existed)." % pid
string += " Confirm this worked using @ps."
source_object.emit_to(string)
GLOBAL_CMD_TABLE.add_command("@testevent", cmd_testevent,
auto_help_override=False)
#------------------------------------------------------------
# Test of Cache system
#------------------------------------------------------------
def cmd_testcache(command):
#
# Tests the cache system by writing to it
# back and forth several times.
#
# Usage:
# @testcache [get]
#
# Use without 'get' to store test data in
# caches and with 'get' to read them back
# and make sure they all saved as they
# should. You might also want to
# try shut down the server between
# calls to make sure the persistent
# cache does survive the shutdown.
from src.cache import cache
from src import gametime
source_object = command.source_object
switches = command.command_switches
s1 = "Value: Cache: OK"
s2 = "Value: PCache 1 (set using property assignment): OK"
s3 = "Value: PCache 2 (set using function call): OK"
if switches and "get" in switches:
# Reading from cache
source_object.emit_to("Reading from cache ...")
cache.load_pcache()
cache_vol = source_object.cache.testcache
source_object.emit_to("< volatile cache:\n %s" % cache_vol)
cache_perm = source_object.pcache.testcache_perm
source_object.emit_to("< persistent cache 1/2:\n %s" % cache_perm)
cache_perm2 = cache.get_pcache("permtest2")
source_object.emit_to("< persistent cache 2/2:\n %s" % cache_perm2)
else:
# Saving to cache
source_object.emit_to("Save to cache ...")
source_object.cache.testcache = s1
# using two different ways to set pcache
source_object.pcache.testcache_perm = s2
cache.set_pcache("permtest2", s3)
source_object.emit_to("> volatile cache:\n %s" % s1)
source_object.emit_to("> persistent cache 1/2:\n %s" % s2)
source_object.emit_to("> persistent cache 2/2:\n %s" % s3)
cache.save_pcache()
string = "Caches saved. Use /get as a switch to read them back."
source_object.emit_to(string)
source_object.emit_to("Running Gametime: %i" % gametime.time())
GLOBAL_CMD_TABLE.add_command("@testcache", cmd_testcache,
auto_help_override=False)

View file

@ -1,333 +0,0 @@
"""
Example of using the state system. The State system allows a player
object to be 'trapped' in a special environment where different
commands are available than normal. This is very useful in order to
implement anything from menus and combat states to npc-conversational
choices and inline text-editors.
This example uses the State system to create a simple menu.
To test out this example, add this module to the
CUSTOM_COMMAND_MODULES tuple in your game/settings.py as
'game.gamesrc.commands.examples.state_example' (see ./example.py for
another example). You need to restart the Evennia server before new
files are recognized.
Next enter the mud and give the command
> @testmenu
Note that the help entries related to this little menu are not part of
the normal help database, they are stored with the state and only
accessible from inside it. Try 'help entermenu' from outside the state
and 'help' and 'info' from inside the menu to see the auto-help system
in action.
To further test the state system, try the command
> @teststate
This takes arguments between 1-6 to set up various states with varying
access to different global commands.
See also misc_tests.py for other tests.
"""
# This is the normal command table, accessible by default
from src.cmdtable import GLOBAL_CMD_TABLE
# The statetable contains sets of cmdtables that is made available
# only when we are in a particular state (possibly overriding
# same-named commands in GLOBAL_CMD_TABLE).
from src.statetable import GLOBAL_STATE_TABLE
#
# Implementing a simple 'menu' state
#
#the name of our state, to make sure it's the same everywhere
STATENAME = 'menu'
#
# 'entry' command. This takes the player from the normal game
# mode into the menu state. This must be added to the
# GLOBAL_CMD_TABLE like any other command.
#
def cmd_entermenu(command):
"""
entermenu - enter the example menu
Usage:
entermenu
This is the 'entry' command that takes the player from the normal
gameplay mode into the 'menu' state.
"""
# get the player object calling the command
source_object = command.source_object
# this is important: we use the set_state() command
# to shift the player into a state named 'menu'. Other useful
# access functions on source_object are get_state()
# and clear_state(), the latter returns the player to
# the normal mode of gameplay.
source_object.set_state(STATENAME)
#show a welcome text .
string = """
Welcome to the Demo menu! In this small demo all you can do is
select one of the two options so it changes colour. This is just
intended to show off the possibilities of the state system. More
interesting things should of course happen in a real menu.
Use @exit to leave the menu.
"""
source_object.emit_to(string)
# show the menu
source_object.execute_cmd('menu')
#
# Below are commands only available while in the 'menu' state.
# Note that the _doc__ strings of the functions
# can be read as help entries when in the menu.
#
def menu_cmd_option1(command):
"""
option1
This command, obviously, selects the first option.
"""
source_object = command.source_object
print_menu(source_object, 1)
def menu_cmd_option2(command):
"""
option2
This command selects the second option. Duh.
"""
source_object = command.source_object
print_menu(source_object, 2)
def menu_cmd_menu(command):
"""
menu
Clears the options and redraws the menu.
[[autohelp]]
Auto-help
This is an extra topic to test the auto-help functionality. The state-help
system supports nested ('related') topics using [ [subtopic] ] markup,
just like the normal help index does.
"""
source_object = command.source_object
print_menu(source_object)
#
# helper function
#
def print_menu(source_obj, choice=None):
"""
Utility function to print the menu. More interesting things
would happen here in a real menu.
"""
if choice == 1:
#ansi colouring; see src.ansi
chtext = "%s> option1\n %soption2" % ('%ch%cy','%cn%cy')
elif choice == 2:
chtext = " %soption1\n%s> option2" % ('%cn%cy','%ch%cy')
else:
chtext = " %soption1\n option2" % ('%cn%cy')
string ="\n%sMenu: \n%s\n %shelp \n @exit" % ('%ch%cr', chtext, '%cn%cy')
source_obj.emit_to(string)
# Add the 'entry' command to the normal command table
GLOBAL_CMD_TABLE.add_command("@testmenu", cmd_entermenu,
auto_help_override=False)
# create the state. We make sure the player can exit it at
# any time by @exit.
GLOBAL_STATE_TABLE.add_state(STATENAME, exit_command=True)
# Add the menu commands to the state table by tying them to the 'menu'
# state. It is important that the name of the state matches what we
# set the player-object to in the 'entry' command.
GLOBAL_STATE_TABLE.add_command(STATENAME, "option1", menu_cmd_option1)
GLOBAL_STATE_TABLE.add_command(STATENAME, "option2", menu_cmd_option2)
GLOBAL_STATE_TABLE.add_command(STATENAME, "menu", menu_cmd_menu)
#
# enterstate - testing the depth of the state system
#
# This is a test suite that shows off all the features of the state
# system. It sets up a test command @test_state that takes an
# argument 1-6 for moving into states with different
# characteristics. Note that the only difference as to how the various
# states are created lies in the options given to the add_state()
# function. Use @exit to leave any state.
# defining the test-state names so they are the same everywhere
TSTATE1 = 'no_globals'
TSTATE2 = 'all_globals'
TSTATE3 = 'include_some_globals'
TSTATE4 = 'exclude_some_globals'
TSTATE5 = 'global_allow_exits'
TSTATE6 = 'noglobal_allow_exits_obj_cmds'
#
#the test command 'enterstate'
#
def cmd_test_state(command):
"""
@teststate - testing the state system
Usage: @teststate [1 - 6]
Give arguments 1-6 to enter different game states. Use @exit to
get out of the state at any time.
1: A very limited state; only contains the 'test' state command.
2: All global commands are included (so this should be the same as
normal operation, except you cannot traverse exits and use
object-based cmds)
3: /Only/ the global commands 'get' and 'inventory' are included
into the state.
4: All global commands /except/ 'get' and 'inventory' are available
5: All global commands availabe + ability to traverse exits (not use
object-based cmds).
6: Only the 'test' command available, but ability to
both traverse exits and use object-based cmds.
Ideas for in-game use:
1: Try out the '@testmenu' command for an example of this state.
2: Could be used in order to stop someone from moving despite exits
being open (tied up? In combat?)
3: someone incapacitated or blinded might get only limited commands
available
4: in e.g. a combat state, things like crafting should not be
possible.
5: Pretty much default operation, just removing some global commands.
Maybe limiting the use of magical weapons in a room or similar.
6: A state of panic - You can move, but not take in your surroundings.
... the possibilities are endless.
"""
source_object = command.source_object
args = command.command_argument
# check for missing arguments
if not args:
source_object.emit_to("Usage: @teststate [1 - 6]")
return
# build up a return string
string = "\n Entering state ... \nThis state includes the"
string += " commands 'test', 'help', '@exit' and "
arg = args.strip()
# step through the various options
if arg == '1':
string += "no global commands at all. \nWith some more state commands, "
string += "this state would work well for e.g. a "
string += "combat state or a menu where the player don't need access "
string += "to the normal command definitions. Take a special "
string += "look at the help command, which is in fact a "
string += "state-only version of the normal help."
state = TSTATE1
elif arg == '2':
string += "all global commands. You should be able to do "
string += "everything as normal, but not move around."
state = TSTATE2
elif arg == '3':
string += "the global commands 'inv' and 'get' only."
state = TSTATE3
elif arg == '4':
string += "all global commands *except* 'inv' and 'get' (try "
string += "using them). \nThis allows you to disable commands that "
string += "should not be possible at a certain time (like starting "
string += "to craft while in the middle of a fight or something)."
state = TSTATE4
elif arg == '5':
string += "all global commands as well as the ability to traverse "
string += "exits. You do not have the ability to use commands "
string += "defined on objects though."
state = TSTATE5
elif arg == '6':
string += "no globals at all, but you have the ability to both "
string += "use exits and commands on items. \nThis would maybe be "
string += "interesting for a 'total darkness' state or maybe a "
string += "'panic' state where you can move around but cannot "
string += "actually take in your surroundings."
state = TSTATE6
else:
source_object.emit_to("Usage: enterstate 1 - 6")
return
#set the state
source_object.set_state(state)
info = "%s\n (Now in state %s: '%s' ... use @exit to leave the state.)"
source_object.emit_to(info % (string, arg, state))
#
# define a simple command to include in all states.
#
def cmd_instate_cmd(command):
"""
test
Usage:
test
This is the help text for the test command (created with the
auto_help sytem). This is a state-only command that does not
exist outside this state. Since this state is completely isolated
from the normal gameplay, commands can also harmlessly redefine
any normal command - so if there was a normal command named
'test', it would remain unchanged when we leave the state.
"""
command.source_object.emit_to("This state command (test) works!")
#
# Create the test states
#
#define some global commands to filter for
CMDFILTER = ['get', 'inventory']
#1: A simple, basic state with no global commands
GLOBAL_STATE_TABLE.add_state(TSTATE1, exit_command=True)
#2: Include all normal commands in the state
GLOBAL_STATE_TABLE.add_state(TSTATE2, exit_command=True, global_cmds='all')
#3: Include only the two global commands in cmdfilter
GLOBAL_STATE_TABLE.add_state(TSTATE3, exit_command=True,
global_cmds='include', global_filter=CMDFILTER)
#4: Include all global commands except the ones in cmdfilter
GLOBAL_STATE_TABLE.add_state(TSTATE4, exit_command=True,
global_cmds='exclude', global_filter=CMDFILTER)
#5: Include all global commands + ability to traverse exits
GLOBAL_STATE_TABLE.add_state(TSTATE5, exit_command=True,
global_cmds='all',
allow_exits=True)
#6: No global commands, allow exits and commands defined on objects.
GLOBAL_STATE_TABLE.add_state(TSTATE6, exit_command=True,
allow_exits=True, allow_obj_cmds=True)
#append the "test" function to all states
GLOBAL_STATE_TABLE.add_command(TSTATE1, 'test', cmd_instate_cmd)
GLOBAL_STATE_TABLE.add_command(TSTATE2, 'test', cmd_instate_cmd)
GLOBAL_STATE_TABLE.add_command(TSTATE3, 'test', cmd_instate_cmd)
GLOBAL_STATE_TABLE.add_command(TSTATE4, 'test', cmd_instate_cmd)
GLOBAL_STATE_TABLE.add_command(TSTATE5, 'test', cmd_instate_cmd)
GLOBAL_STATE_TABLE.add_command(TSTATE6, 'test', cmd_instate_cmd)
#create the entry function for testing all states
GLOBAL_CMD_TABLE.add_command('@teststate', cmd_test_state)

View file

@ -1,67 +0,0 @@
"""
Example of the event system. To try it out, make sure to import it from somewhere
covered by @reload (like the script parent). Create an object inheriting
the red_button parent to see its effects (e.g. @create button=examples/red_button)
Technically the event don't contain any game logics, all it does is locate all
objects inheriting to a particular script parent and calls one of its functions
at a regular interval.
Note that this type of event will cause *all* red buttons to blink at the same
time, regardless when they were created. This is a very efficient way
to do it (it is also very useful for global events like weather patterns
and day-night cycles), but you can also add events directly to individual objecs
(see the example event in gamesrc/parents/examples/red_button)
"""
import traceback
from src.events import IntervalEvent
from src import scheduler
from src.objects.models import Object
#the logger is useful for debugging
from src import logger
class EventBlinkButton(IntervalEvent):
"""
This event lets the button flash at regular intervals.
"""
def __init__(self):
"""
Note that we do NOT make this event persistent across
reboots since we are actually creating it (i.e. restarting it)
every time the module is reloaded.
"""
super(EventBlinkButton, self).__init__()
self.name = 'event_blink_red_button'
#how often to blink, in seconds
self.interval = 30
#the description is seen when you run @ps in-game.
self.description = "Blink red buttons regularly."
def event_function(self):
"""
This stub function is automatically fired every self.interval seconds.
In this case we do a search for all objects inheriting from the correct
parent and call a function on them.
Note that we must make sure to handle all tracebacks in this
function to avoid trouble.
"""
#find all objects inheriting from red_button (parents are per definition
#stored with the gamesrc/parent/ drawer as a base)
parent = 'examples.red_button'
buttons = Object.objects.global_object_script_parent_search(parent)
for b in buttons:
try:
b.scriptlink.blink()
except:
# Print all tracebacks to the log instead of letting them by.
# This is important, we must handle these exceptions gracefully!
logger.log_errmsg(traceback.print_exc())
#create and add the event to the global handler
blink_event = EventBlinkButton()
scheduler.add_event(blink_event)

View file

@ -0,0 +1,158 @@
"""
These are the base object typeclasses, a convenient shortcut to the
objects in src/objects/objects.py. You can start building your game
from these bases if you want.
To change these defaults to point to some other object,
change some or all of these variables in settings.py:
BASE_OBJECT_TYPECLASS
BASE_CHARACTER_TYPECLASS
BASE_ROOM_TYPECLASS
BASE_EXIT_TYPECLASS
BASE_PLAYER_TYPECLASS
Some of the main uses for these settings are not hard-coded in
Evennia, rather they are convenient defaults for in-game commands
(which you may change) Example would be build commands like '@dig'
knowing to create a particular room-type object).
New instances of Objects (inheriting from these typeclasses)
are created with src.utils.create.create_object(typeclass, ...)
where typeclass is the python path to the class you want to use.
"""
from src.objects.objects import Object as BaseObject
from src.objects.objects import Character as BaseCharacter
from src.objects.objects import Room as BaseRoom
from src.objects.objects import Exit as BaseExit
from src.players.player import Player as BasePlayer
class Object(BaseObject):
"""
This is the root typeclass object, implementing an in-game Evennia
game object, such as having a location, being able to be
manipulated or looked at, etc. If you create a new typeclass, it
must always inherit from this object (or any of the other objects
in this file, since they all actually inherit from BaseObject, as
seen in src.object.objects).
The BaseObject class implements several hooks tying into the game
engine. By re-implementing these hooks you can control the
system. You should never need to re-implement special Python
methods, such as __init__ and especially never __getattribute__ and
__setattr__ since these are used heavily by the typeclass system
of Evennia and messing with them might well break things for you.
Hooks (these are class methods, so their arguments should also start with self):
at_object_creation() - only called once, when object is first created.
Almost all object customizations go here.
at_first_login() - only called once, the very first time user logs in.
at_pre_login() - called every time the user connects, after they have
identified, just before the system actually logs them in.
at_post_login() - called at the end of login, just before setting the
player loose in the world.
at_disconnect() - called just before the use is disconnected (this is also
called if the system determines the player lost their link)
at_object_delete() - called just before the database object is permanently
deleted from the database with obj.delete(). Note that cleaning out contents
and deleting connected exits is not needed, this is handled
automatically when doing obj.delete(). If this method returns
False, deletion is aborted.
at_before_move(destination) - called by obj.move_to() just before moving object to the destination.
If this method returns False, move is cancelled.
announce_move_from(destination) - called while still standing in the old location,
if obj.move_to() has argument quiet=False.
announce_move_to(source_location) - called after move, while standing in the new location
if obj.move_to() has argument quiet=False.
at_after_move(source_location) - always called after a move has been performed.
at_object_leave(obj, target_location) - called when this object loose an object (e.g.
someone leaving the room, an object is given away etc)
at_object_receive(obj, source_location) - called when this object receives another object
(e.g. a room being entered, an object moved into inventory)
return_appearance(looker) - by default, this is used by the 'look' command to
request this object to describe itself. Looker
is the object requesting to get the information.
at_desc(looker=None) - by default called whenever the appearance is requested.
"""
pass
class Character(BaseCharacter):
"""
This is the default object created for a new user connecting - the
in-game player character representation. Note that it's important
that at_object_creation sets up an script that adds the Default
command set whenever the player logs in - otherwise they won't be
able to use any commands!
"""
def at_object_creation(self):
# This adds the default cmdset to the player every time they log
# in. Don't change this unless you really know what you are doing.
#self.scripts.add(scripts.AddDefaultCmdSet)
super(Character, self).at_object_creation()
# expand with whatever customizations you want below...
# ...
class Room(BaseRoom):
"""
Rooms are like any object, except their location is None
(which is default). Usually this object should be
assigned to room-building commands by use of the
settings.BASE_ROOM_TYPECLASS variable.
"""
pass
class Exit(BaseExit):
"""
Exits are connectors between rooms. They are identified by the
engine by having an attribute "_destination" defined on themselves,
pointing to a valid room object. That is usually defined when
the exit is created (in, say, @dig or @link-type commands), not
hard-coded in their typeclass. Exits do have to make sure they
clean up a bit after themselves though, easiest accomplished
by letting by_object_delete() call the object's parent.
"""
def at_object_delete(self):
"""
The game needs to do some cache cleanups when deleting an exit,
so we make sure to call super() here. If this method returns
False, the deletion is aborted.
"""
# handle some cleanups
return super(Exit, self).at_object_delete()
# custom modifications below.
# ...
class Player(BasePlayer):
"""
This class describes the actual OOC player (i.e. the user connecting
to the MUD). It does NOT have visual appearance in the game world (that
is handled by the character which is connected to this). Comm channels
are attended/joined using this object.
It can be useful e.g. for storing configuration options for your game, but
should generally not hold any character-related info (that's best handled
on the character level).
Can be set using BASE_PLAYER_TYPECLASS.
The following hooks are called by the engine. Note that all of the following
are called on the character object too, and mostly at the same time.
at_player_creation() - This is called once, the very first time
the player is created (i.e. first time they
register with the game). It's a good place
to store attributes all players should have,
like configuration values etc.
at_pre_login() - called every time the user connects, after they have
identified, just before the system actually logs them in.
at_post_login() - called at the end of login, just before setting the
player loose in the world.
at_disconnect() - called just before the use is disconnected (this is also
called if the system determines the player lost their link)
"""
pass

View file

@ -0,0 +1,168 @@
"""
An example script parent for a nice red button object. It has
custom commands defined on itself that are only useful in relation to this
particular object. See example.py in gamesrc/commands for more info
on the pluggable command system.
Assuming this script remains in gamesrc/parents/examples, create an object
of this type using @create button:examples.red_button
This file also shows the use of the Event system to make the button
send a message to the players at regular intervals. To show the use of
Events, we are tying two types of events to the red button, one which cause ALL
red buttons in the game to blink in sync (gamesrc/events/example.py) and one
event which cause the protective glass lid over the button to close
again some time after it was opened.
Note that if you create a test button you must drop it before you can
see its messages!
"""
import random
from game.gamesrc.objects.baseobjects import Object
from game.gamesrc.scripts.examples import red_button_scripts as scriptexamples
from game.gamesrc.commands.examples import cmdset_red_button as cmdsetexamples
#
# Definition of the object itself
#
class RedButton(Object):
"""
This class describes an evil red button.
It will use the script definition in
game/gamesrc/events/example.py to blink
at regular intervals until the lightbulb
breaks. It also use the EventCloselid script to
close the lid and do nasty stuff when pressed.
"""
def at_object_creation(self):
"""
This function is called when object is created. Use this
instead of e.g. __init__.
"""
# store desc
desc = "This is a large red button, inviting yet evil-looking. "
desc += "A closed glass lid protects it."
self.db.desc = desc
# We have to define all the variables the scripts
# are checking/using *before* adding the scripts or
# they might be deactivated before even starting!
self.db.lid_open = False
self.db.lamp_works = True
self.db.lid_locked = False
# set the default cmdset to the object, permanent=True means a
# script will automatically be created to always add this.
self.cmdset.add_default(cmdsetexamples.DefaultCmdSet, permanent=True)
# since the other cmdsets relevant to the button are added 'on the fly',
# we need to setup custom scripts to do this for us (also, these scripts
# check so they are valid (i.e. the lid is actually still closed)).
# The AddClosedCmdSet script makes sure to add the Closed-cmdset.
self.scripts.add(scriptexamples.ClosedLidState)
# the script EventBlinkButton makes the button blink regularly.
self.scripts.add(scriptexamples.BlinkButtonEvent)
# state-changing methods
def open_lid(self, feedback=True):
"""
Open the glass lid and start the timer so it will soon close
again.
"""
if self.db.lid_open:
return
desc = "This is a large red button, inviting yet evil-looking. "
desc += "Its glass cover is open and the button exposed."
self.db.desc = desc
self.db.lid_open = True
if feedback and self.location:
string = "The lid slides clear of the button with a click."
string += "\nA ticking sound is heard, suggesting the lid might have"
string += " some sort of timed locking mechanism."
self.location.msg_contents(string)
# with the lid open, we validate scripts; this will clean out
# scripts that depend on the lid to be closed.
self.scripts.validate()
# now add new scripts that define the open-lid state
self.obj.scripts.add(scriptexamples.OpenLidState)
# we also add a scripted event that will close the lid after a while.
# (this one cleans itself after being called once)
self.scripts.add(scriptexamples.CloseLidEvent)
def close_lid(self, feedback=True):
"""
Close the glass lid. This validates all scripts on the button,
which means that scripts only being valid when the lid is open
will go away automatically.
"""
if not self.db.lid_open:
return
desc = "This is a large red button, inviting yet evil-looking. "
desc += "Its glass cover is closed, protecting it."
self.db.desc = desc
self.db.lid_open = False
if feedback and self.location:
string = "With a click the lid slides back, securing the button once again."
self.location.msg_contents(string)
# clean out scripts depending on lid to be open
self.scripts.validate()
# add scripts related to the closed state
self.scripts.add(scriptexamples.ClosedLidState)
def break_lamp(self, feedback=True):
"""
Breaks the lamp in the button, stopping it from blinking.
"""
self.db.lamp_works = False
self.obj.db.desc = "The big red button has stopped blinking for the time being."
if feedback and self.location:
string = "The lamp flickers, the button going dark."
self.location.msg_contents(string)
self.scripts.validate()
def press_button(self, pobject):
"""
Someone was foolish enough to press the button!
pobject - the person pressing the button
"""
# deactivate the button so it won't flash/close lid etc.
self.scripts.add(scriptexamples.DeactivateButtonEvent)
# blind the person pressing the button. Note that this
# script is set on the *character* pressing the button!
pobject.scripts.add(scriptexamples.BlindedState)
# script-related methods
def blink(self):
"""
The script system will regularly call this
function to make the button blink. Now and then
it won't blink at all though, to add some randomness
to how often the message is echoed.
"""
loc = self.location
if loc:
rand = random.random()
if rand < 0.2:
string = "The red button flashes briefly."
elif rand < 0.4:
string = "The red button blinks invitingly."
elif rand < 0.6:
string = "The red button flashes. You know you wanna push it!"
else:
# no blink
return
loc.msg_contents(string)

View file

@ -1,6 +0,0 @@
Do not modify files in this directory! They are base classes from which other
things may be sub-classed. Modifying these classes may cause conflicts the
next time you upgrade manually or through subversion.
Instead, sub-class the classes contained here and point the default parent
imports in settings.py to your new classes.

View file

@ -1,27 +0,0 @@
"""
This is the base object type/interface that all parents are derived from by
default. Each object type sub-classes this class and over-rides methods as
needed.
NOTE: This file should NOT be directly modified. Sub-class this in
your own class in game/gamesrc/parents and change
SCRIPT_DEFAULT_OBJECT variable in settings.py to point to the new class.
"""
from src.script_parents.basicobject import EvenniaBasicObject
class BasicObject(EvenniaBasicObject):
pass
def class_factory(source_obj):
"""
This method is called any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
Since this is the only place where the object is actually instantiated,
this is also the place to put commands you want to act on this object,
do this by obj.command_table.add_command('cmd', cmd_def).
"""
obj = BasicObject(source_obj)
return obj

View file

@ -1,21 +0,0 @@
"""
This is the basic Evennia-standard player parent.
NOTE: This file should NOT be directly modified. Sub-class the BasicPlayer
class in your own class in game/gamesrc/parents and change the
SCRIPT_DEFAULT_PLAYER variable in settings.py to point to the new class.
"""
from src.script_parents.basicobject import EvenniaBasicObject
from src.script_parents.basicplayer import EvenniaBasicPlayer
class BasicPlayer(EvenniaBasicObject, EvenniaBasicPlayer):
pass
def class_factory(source_obj):
"""
This method is called any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return BasicPlayer(source_obj)

View file

@ -1,75 +0,0 @@
"""
Simple example of a custom modified object, derived from the base object.
If you want to make this your new default object type, move this into
gamesrc/parents and set SCRIPT_DEFAULT_OBJECT = 'custom_basicobject'
in game/settings.py.
Generally, if you want to conveniently set future objects to inherit from this
script parent, this file and others like it need to be
located under the game/gamesrc/parent directory.
"""
from game.gamesrc.parents.base.basicobject import BasicObject
class CustomBasicObject(BasicObject):
"""
This defines the base class of all non-player objects in game.
"""
def at_object_creation(self):
"""
This function is called whenever the object is created. Use
this instead of __init__ to set start attributes etc on a
particular object type.
"""
#Set an "sdesc" (short description) attribute on object,
#defaulting to its given name
#get the stored object related to this class
obj = self.scripted_obj
#find out the object's name
name = obj.get_name(fullname=False,
show_dbref=False,
show_flags=False)
#assign the name to the new attribute
obj.set_attribute('sdesc', name)
def at_object_destruction(self, pobject=None):
"""
This is triggered when an object is about to be destroyed via
@destroy ONLY. If an object is deleted via delete(), it is assumed
that this method is to be skipped.
values:
* pobject: (Object) The object requesting the action.
"""
pass
def at_before_move(self, target_location):
"""
This hook is called just before the object is moved.
Input:
target_location (obj): The location the player is about to move to.
Return value:
If this function returns anything but None (no return value),
the move is aborted. This allows for character-based move
restrictions (not only exit locks).
"""
pass
def at_after_move(self):
"""
This hook is called just after the object has been successfully moved.
"""
pass
def class_factory(source_obj):
"""
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return CustomBasicObject(source_obj)

View file

@ -1,76 +0,0 @@
"""
This is an example of customizing the basic player character object.
You will want to do this to add all sorts of custom things like
attributes, skill values, injuries and so on.
If you want to make this the default player object for all players,
move it into gamesrc/parents and set SCRIPT_DEFAULT_PLAYER =
'custom_basicplayer' in game/settings.py.
"""
from game.gamesrc.parents.base.basicplayer import BasicPlayer
class CustomBasicPlayer(BasicPlayer):
"""
This is the base class for all players in game.
"""
def at_player_creation(self):
"""
Called when player object is first created. Use this
instead of __init__ to define any custom attributes
all your player characters should have.
"""
#Example: Adding a default sdesc (short description)
#get the stored object related to this class
pobject = self.scripted_obj
#set the attribute
pobject.set_attribute('sdesc', 'A normal person')
def at_pre_login(self, session):
"""
Called when the player has entered the game but has not
logged in yet.
"""
pass
def at_post_login(self, session):
"""
This command is called after the player has logged in but
before he is allowed to give any commands.
"""
#get the object linked to this class
pobject = self.scripted_obj
#find out more about our object
name = pobject.get_name(fullname=False,
show_dbref=False,
show_flags=False)
sdesc = pobject.get_attribute_value('sdesc')
#send a greeting using our new sdesc attribute
pobject.emit_to("You are now logged in as %s - %s." % (name, sdesc))
#tell everyone else we're here
pobject.get_location().emit_to_contents("%s - %s, has connected." %
(name, sdesc), exclude=pobject)
#show us our surroundings
pobject.execute_cmd("look")
def at_move(self):
"""
This is triggered whenever the object is moved to a new location
(for whatever reason) using the src.objects.models.move_to() function.
"""
pass
def class_factory(source_obj):
"""
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
"""
return CustomBasicPlayer(source_obj)

View file

@ -1,220 +0,0 @@
"""
An example script parent for a nice red button object. It has
custom commands defined on itself that are only useful in relation to this
particular object. See example.py in gamesrc/commands for more info
on the pluggable command system.
Assuming this script remains in gamesrc/parents/examples, create an object
of this type using @create button:examples.red_button
This file also shows the use of the Event system to make the button
send a message to the players at regular intervals. To show the use of
Events, we are tying two types of events to the red button, one which cause ALL
red buttons in the game to blink in sync (gamesrc/events/example.py) and one
event which cause the protective glass lid over the button to close
again some time after it was opened.
Note that if you create a test button you must drop it before you can
see its messages!
"""
import traceback
from game.gamesrc.parents.base.basicobject import BasicObject
from src.objects.models import Object
from src.events import IntervalEvent
from src import scheduler
from src import logger
#
# Events
#
# Importing this will start the blink event ticking, only one
# blink event is used for all red buttons.
import game.gamesrc.events.example
# We also create an object-specific event.
class EventCloselid(IntervalEvent):
"""
This event closes the glass lid over the button
some time after it was opened.
"""
def __init__(self, obj):
"""
Note how we take an object as an argument,
this will allow instances of this event to
operate on this object only.
"""
# we must call super to make sure things work!
super(EventCloselid, self).__init__()
# store the object reference
self.obj_dbref = obj.dbref()
# This is used in e.g. @ps to show what the event does
self.description = "Close lid on %s" % obj
# We make sure that this event survives a reboot
self.persistent = True
# How many seconds from event creation to closing
# the lid
self.interval = 20
# We only run the event one time before it deletes itself.
self.repeats = 1
def event_function(self):
"""
This function is called every self.interval seconds.
Note that we must make sure to handle all errors from
this call to avoid trouble.
"""
try:
# if the lid is open, close it. We have to find the object
# again since it might have changed.
obj = Object.objects.get_object_from_dbref(self.obj_dbref)
if obj.has_flag("LID_OPEN"):
obj.scriptlink.close_lid()
retval = "The glass cover over the button silently closes by itself."
obj.get_location().emit_to_contents(retval)
except:
# send the traceback to the log instead of letting it by.
# It is important that we handle exceptions gracefully here!
logger.log_errmsg(traceback.print_exc())
#
# Object commands
#
# Commands for using the button object. These are added to
# the object in the class_factory function at the
# bottom of this module.
#
def cmd_open_lid(command):
"""
Open the glass lid cover over the button.
"""
# In the case of object commands, you can use this to
# get the object the command is defined on.
obj = command.scripted_obj
if obj.has_flag("LID_OPEN"):
retval = "The lid is already open."
else:
retval = "You lift the lid, exposing the tempting button."
obj.scriptlink.open_lid()
command.source_object.emit_to(retval)
def cmd_close_lid(command):
"""
Close the lid again.
"""
obj = command.scripted_obj
if not obj.has_flag("LID_OPEN"):
retval = "The lid is already open."
else:
retval = "You secure the glass cover over the button."
obj.scriptlink.close_lid()
command.source_object.emit_to(retval)
def cmd_push_button(command):
"""
This is a simple command that handles a user pressing the
button by returning a message. The button can only be
"""
obj = command.scripted_obj
if obj.has_flag("LID_OPEN"):
retval = "You press the button ..."
retval += "\n ..."
retval += "\n BOOOOOM!"
obj.scriptlink.close_lid()
else:
retval = "There is a glass lid covering "
retval += "the button as a safety measure. If you "
retval += "want to press the button you need to open "
retval += "the lid first."
command.source_object.emit_to(retval)
#
# Definition of the object itself
#
class RedButton(BasicObject):
"""
This class describes an evil red button.
It will use the event definition in
game/gamesrc/events/example.py to blink
at regular intervals until the lightbulb
breaks. It also use the EventCloselid event defined
above to close the lid
"""
def at_object_creation(self):
"""
This function is called when object is created. Use this
preferably over __init__.
"""
#get stored object related to this class
obj = self.scripted_obj
obj.set_attribute('desc', "This is a big red button. It has a glass cover.")
obj.set_attribute("breakpoint", 5)
obj.set_attribute("count", 0)
# add the object-based commands to the button
obj.add_command("open lid", cmd_open_lid)
obj.add_command("lift lid", cmd_open_lid)
obj.add_command("close lid", cmd_close_lid)
obj.add_command("push button", cmd_push_button)
obj.add_command("push the button", cmd_push_button)
def open_lid(self):
"""
Open the glass lid and start the timer so it will
soon close again.
"""
self.scripted_obj.set_flag("LID_OPEN")
scheduler.add_event(EventCloselid(self.scripted_obj))
def close_lid(self):
"""
Close the glass lid
"""
self.scripted_obj.unset_flag("LID_OPEN")
def blink(self):
"""
If the event system is active, it will regularly call this
function to make the button blink. Note the use of attributes
to store the variable count and breakpoint in a persistent
way.
"""
obj = self.scripted_obj
try:
count = int(obj.get_attribute_value("count"))
breakpoint = int(obj.get_attribute_value("breakpoint"))
except TypeError:
return
if count <= breakpoint:
if int(count) == int(breakpoint):
string = "The button flashes, then goes dark. "
string += "Looks like the lamp just broke."
else:
string = "The red button flashes, demanding your attention."
count += 1
obj.set_attribute("count", count)
obj.get_location().emit_to_contents(string)
def class_factory(source_obj):
"""
This method is called by any script you retrieve (via the scripthandler). It
creates an instance of the class and returns it transparently.
source_obj: (Object) A reference to the object being scripted (the child).
This is a good place for adding new commands to the button since this is
where it is actually instantiated.
"""
return RedButton(source_obj)

View file

@ -0,0 +1,63 @@
"""
The base object to inherit from when implementing new Scripts.
Scripts are objects that handle everything in the game having
a time-component (i.e. that may change with time, with or without
a player being involved in the change). Scripts can work like "events",
in that they are triggered at regular intervals to do a certain script,
but an Script set on an object can also be responsible for silently
checking if its state changes, so as to update it. Evennia use several
in-built scripts to keep track of things like time, to clean out
dropped connections etc.
New Script objects (from these classes) are created using the
src.utils.create.create_script(scriptclass, ...) where scriptclass
is the python path to the specific class of script you want to use.
"""
from src.scripts.scripts import Script as BaseScript
class Script(BaseScript):
"""
All scripts should inherit from this class and implement
some or all of its hook functions and variables.
Important variables controlling the script object:
self.key - the name of all scripts inheriting from this class
(defaults to <unnamed>), used in lists and searches.
self.desc - a description of the script, used in lists
self.interval (seconds) - How often the event is triggered and calls self.at_repeat()
(see below) Defaults to 0 - that is, never calls at_repeat().
self.start_delay (True/False). If True, will wait self.interval seconds
befor calling self.at_repeat() for the first time. Defaults to False.
self.repeats - The number of times at_repeat() should be called before automatically
stopping the script. Default is 0, which means infinitely many repeats.
self.persistent (True/False). If True, the script will survive a server restart
(defaults to False).
self.obj (game Object)- this ties this script to a particular object. It is
usually not needed to set this parameter explicitly; it's set in the
create methods.
Hook methods (should also include self as the first argument):
at_script_creation() - called only once, when an object of this class
is first created.
is_valid() - is called to check if the script is valid to be running
at the current time. If is_valid() returns False, the running
script is stopped and removed from the game. You can use this
to check state changes (i.e. an script tracking some combat
stats at regular intervals is only valid to run while there is
actual combat going on).
at_start() - Called every time the script is started, which for persistent
scripts is at least once every server start. Note that this is
unaffected by self.delay_start, which only delays the first call
to at_repeat().
at_repeat() - Called every self.interval seconds. It will be called immediately
upon launch unless self.delay_start is True, which will delay
the first call of this method by self.interval seconds. If
self.interval==0, this method will never be called.
at_stop() - Called as the script object is stopped and is about to be removed from
the game, e.g. because is_valid() returned False.
"""
pass

View file

@ -0,0 +1,275 @@
"""
Example of scripts.
These are scripts intended for a particular object - the
red_button object type in gamesrc/types/examples. A few variations
on uses of scripts are included.
"""
from game.gamesrc.scripts.basescript import Script
from game.gamesrc.commands.examples import cmdset_red_button as cmdsetexamples
#
# Scripts as state-managers
#
# Scripts have many uses, one of which is to statically
# make changes when a particular state of an object changes.
# There is no "timer" involved in this case (although there could be),
# whenever the script determines it is "invalid", it simply shuts down
# along with all the things it controls.
#
# To show as many features as possible of the script and cmdset systems,
# we will use three scripts controlling one state each of the red_button,
# each with its own set of commands, handled by cmdsets - one for when
# the button has its lid open, and one for when it is closed and a
# last one for when the player pushed the button and gets blinded by
# a bright light. The last one also has a timer component that allows it
# to remove itself after a while (and the player recovers their eyesight).
class ClosedLidState(Script):
"""
This manages the cmdset for the "closed" button state. What this
means is that while this script is valid, we add the RedButtonClosed
cmdset to it (with commands like open, nudge lid etc)
"""
def at_script_creation(self):
"Called when script first created."
self.desc = "Script that manages the closed-state cmdsets for red button."
self.persistent = True
def at_start(self):
"""
This is called once every server restart, so we want to add the
(memory-resident) cmdset to the object here. is_valid is automatically
checked so we don't need to worry about adding the script to an
open lid.
"""
#All we do is add the cmdset for the closed state.
self.obj.cmdset.add(cmdsetexamples.LidClosedCmdSet)
def is_valid(self):
"""
The script is only valid while the lid is closed.
self.obj is the red_button on which this script is defined.
"""
return not self.obj.db.lid_open
def at_stop(self):
"""
When the script stops we must make sure to clean up after us.
"""
self.obj.cmdset.delete(cmdsetexamples.LidClosedCmdSet)
class OpenLidState(Script):
"""
This manages the cmdset for the "open" button state. This will add
the RedButtonOpen
"""
def at_script_creation(self):
"Called when script first created."
self.desc = "Script that manages the opened-state cmdsets for red button."
self.persistent = True
def at_start(self):
"""
This is called once every server restart, so we want to add the
(memory-resident) cmdset to the object here. is_valid is
automatically checked, so we don't need to worry about
adding the cmdset to a closed lid-button.
"""
#print "In Open at_start (should add cmdset)"
self.obj.cmdset.add(cmdsetexamples.LidOpenCmdSet)
def is_valid(self):
"""
The script is only valid while the lid is open.
self.obj is the red_button on which this script is defined.
"""
return self.obj.db.lid_open
def at_stop(self):
"""
When the script stops (like if the lid is closed again)
we must make sure to clean up after us.
"""
self.obj.cmdset.delete(cmdsetexamples.LidOpenCmdSet)
class BlindedState(Script):
"""
This is a timed state.
This adds a (very limited) cmdset TO THE PLAYER, during a certain time,
after which the script will close and all functions are
restored. It's up to the function starting the script to actually
set it on the right player object.
"""
def at_script_creation(self):
"""
We set up the script here.
"""
self.key = "temporary_blinder"
self.desc = "Temporarily blinds the player for a little while."
self.interval = 20 # seconds
self.start_delay = True # we don't want it to stop until after 20s.
self.repeats = 1 # this will go away after interval seconds.
self.persistent = False # we will ditch this if server goes down
def at_start(self):
"""
We want to add the cmdset to the linked object.
Note that the RedButtonBlind cmdset is defined to completly
replace the other cmdsets on the stack while it is active
(this means that while blinded, only operations in this cmdset
will be possible for the player to perform). It is however
not persistent, so should there be a bug in it, we just need
to restart the server to clear out of it during development.
"""
self.obj.cmdset.add(cmdsetexamples.BlindCmdSet)
def at_stop(self):
"""
It's important that we clear out that blinded cmdset
when we are done!
"""
self.obj.msg("Your blink feverishly as your eyesight slowly returns.")
self.obj.location.msg_contents("%s seems to be recovering their eyesight."
% self.obj.name,
exclude=self.obj)
self.obj.cmdset.delete() # this will clear the latest added cmdset,
# (which is the blinded one).
#
# Timer/Event-like Scripts
#
# Scripts can also work like timers, or "events". Below we
# define three such timed events that makes the button a little
# more "alive" - one that makes the button blink menacingly, another
# that makes the lid covering the button slide back after a while.
#
class CloseLidEvent(Script):
"""
This event closes the glass lid over the button
some time after it was opened. It's a one-off
script that should be started/created when the
lid is opened.
"""
def at_script_creation(self):
"""
Called when script object is first created. Sets things up.
We want to have a lid on the button that the user can pull
aside in order to make the button 'pressable'. But after a set
time that lid should auto-close again, making the button safe
from pressing (and deleting this command).
"""
self.key = "lid_closer"
self.desc = "Closes lid on a red buttons"
self.interval = 20 # seconds
self.start_delay = True # we want to pospone the launch.
self.repeats = 1 # we only close the lid once
self.persistent = True # even if the server crashes in those 20 seconds,
# the lid will still close once the game restarts.
def is_valid(self):
"""
This script can only operate if the lid is open; if it
is already closed, the script is clearly invalid.
Note that we are here relying on an self.obj being
defined (and being a RedButton object) - this we should be able to
expect since this type of script is always tied to one individual
red button object and not having it would be an error.
"""
return self.obj.db.lid_open
def at_repeat(self):
"""
Called after self.interval seconds. It closes the lid. Before this method is
called, self.is_valid() is automatically checked, so there is no need to
check this manually.
"""
self.obj.close_lid()
class BlinkButtonEvent(Script):
"""
This timed script lets the button flash at regular intervals.
"""
def at_script_creation(self):
"""
Sets things up. We want the button's lamp to blink at
regular intervals, unless it's broken (can happen
if you try to smash the glass, say).
"""
self.key = "blink_button"
self.desc = "Blinks red buttons"
self.interval = 35 #seconds
self.start_delay = False #blink right away
self.persistent = True #keep blinking also after server reboot
def is_valid(self):
"""
Button will keep blinking unless it is broken.
"""
#print "self.obj.db.lamp_works:", self.obj.db.lamp_works
return self.obj.db.lamp_works
def at_repeat(self):
"""
Called every self.interval seconds. Makes the lamp in
the button blink.
"""
self.obj.blink()
class DeactivateButtonEvent(Script):
"""
This deactivates the button for a short while (it won't blink, won't
close its lid etc). It is meant to be called when the button is pushed
and run as long as the blinded effect lasts. We cannot put these methods
in the AddBlindedCmdSet script since that script is defined on the *player*
whereas this one must be defined on the *button*.
"""
def at_script_creation(self):
"""
Sets things up.
"""
self.key = "deactivate_button"
self.desc = "Deactivate red button temporarily"
self.interval = 21 #seconds
self.start_delay = True # wait with the first repeat for self.interval seconds.
self.persistent = True
self.repeats = 1 # only do this once
def at_start(self):
"""
Deactivate the button. Observe that this method is always
called directly, regardless of the value of self.start_delay
(that just controls when at_repeat() is called)
"""
# closing the lid will also add the ClosedState script
self.obj.close_lid(feedback=False)
# lock the lid so other players can't access it until the
# first one's effect has worn off.
self.obj.db.lid_locked = True
# breaking the lamp also sets a correct desc
self.obj.break_lamp(feedback=False)
def at_repeat(self):
"""
When this is called, reset the functionality of the button.
"""
# restore button's desc.
self.obj.db.lamp_works = True
desc = "This is a large red button, inviting yet evil-looking. "
desc += "Its glass cover is closed, protecting it."
self.db.desc = desc
# re-activate the blink button event.
self.obj.scripts.add(BlinkButtonEvent)
# unlock the lid
self.obj.db.lid_locked = False
self.obj.scripts.validate()

View file

@ -1,123 +0,0 @@
"""
This module offers a collection of useful general functions from the
game engine to make things easier to find.
Just import game.gamesrc.utils and refer to the globals defined herein.
Note that this is not intended as a comprehensive collection, merely
a convenient place to refer to for the methods we have found to be
often used. You will still have to refer to the modules
in evennia/src for more specialized operations.
You will also want to be well familiar with all the facilities each
object offers. The object model is defined in src/objects/models.py.
"""
#------------------------------------------------------------
# imports
#------------------------------------------------------------
from django.conf import settings as in_settings
from src import logger
from src import scheduler as in_scheduler
from src.objects.models import Object
from src import defines_global
from src.cmdtable import GLOBAL_CMD_TABLE as in_GLOBAL_CMD_TABLE
from src.statetable import GLOBAL_STATE_TABLE as in_GLOBAL_STATE_TABLE
from src.events import IntervalEvent as in_IntervalEvent
#------------------------------------------------------------
# Import targets
#------------------------------------------------------------
settings = in_settings
GLOBAL_CMD_TABLE = in_GLOBAL_CMD_TABLE
GLOBAL_STATE_TABLE = in_GLOBAL_STATE_TABLE
# Events
scheduler = in_scheduler
IntervalEvent = in_IntervalEvent
#------------------------------------------------------------
# Log to file/stdio
# log_xxxmsg(msg)
#------------------------------------------------------------
log_errmsg = logger.log_errmsg
log_warnmsg = logger.log_warnmsg
log_infomsg = logger.log_infomsg
#------------------------------------------------------------
# Search methods
#------------------------------------------------------------
# NOTE: All objects also has search_for_object() defined
# directly on themselves, which is a convenient entryway into a
# local and global search with automatic feedback to the
# calling player.
# def get_object_from_dbref(dbref):
# Returns an object when given a dbref.
get_object_from_dbref = Object.objects.get_object_from_dbref
# def dbref_search(dbref_string, limit_types=False):
# Searches for a given dbref.
dbref_search = Object.objects.dbref_search
# def global_object_name_search(ostring, exact_match=True, limit_types=[]):
# Searches through all objects for a name match.
global_object_name_search = Object.objects.global_object_name_search
# def global_object_script_parent_search(script_parent):
# Searches through all objects returning those which has a certain script parent.
global_object_script_parent_search = Object.objects.global_object_script_parent_search
# def player_name_search(searcher, ostring):
# Search players by name.
player_name_search = Object.objects.player_name_search
# def local_and_global_search(searcher, ostring, search_contents=True,
# search_location=True, dbref_only=False,
# limit_types=False, attribute_name=None):
# Searches an object's location then globally for a dbref or name match.
local_and_global_search = Object.objects.local_and_global_search
#------------------------------------------------------------
# Creation commands
#------------------------------------------------------------
# def create_object(name, otype, location, owner, home=None, script_parent=None):
# Create a new object
create_object = Object.objects.create_object
# def copy_object(original_object, new_name=None, new_location=None, reset=False):
# Create and return a new object as a copy of the source object. All will
# be identical to the original except for the dbref. Does not allow the
# copying of Player objects.
copy_object = Object.objects.copy_object
#------------------------------------------------------------
# Validation
#------------------------------------------------------------
# NOTE: The easiest way to check if an object
# is of a particular type is to use each object's
# is_X() function, like is_superuser(), is_thing(),
# is_room(), is_player(), is_exit() and get_type().
OTYPE_NOTHING = defines_global.OTYPE_NOTHING
OTYPE_PLAYER = defines_global.OTYPE_PLAYER
OTYPE_ROOM = defines_global.OTYPE_ROOM
OTYPE_THING = defines_global.OTYPE_THING
OTYPE_EXIT = defines_global.OTYPE_EXIT
OTYPE_GOING = defines_global.OTYPE_GOING
TYPE_GARBAGE = defines_global.OTYPE_GARBAGE
NOPERMS_MSG = defines_global.NOPERMS_MSG
NOCONTROL_MSG = defines_global.NOCONTROL_MSG
# def is_dbref(self, dbstring, require_pound=True):
# Is the input a well-formed dbref number?
is_dbref = Object.objects.is_dbref

View file

@ -3,9 +3,8 @@
#
# It allows batch processing of normal Evennia commands.
# Test it by loading it with the @batchprocess command
# (superuser only):
#
# @batchprocess[/interactive] </full/path/to/this/file>
# @batchprocess[/interactive] examples.batch_example
#
# A # as the first symbol on a line begins a comment and
# marks the end of a previous command definition (important!).
@ -18,12 +17,12 @@
# This creates a red button
@create button
@create button:examples.red_button.RedButton
# This comment ends input for @create
# Next command:
@set button=desc:
@set button/desc =
This is a large red button. Now and then
it flashes in an evil, yet strangely tantalizing way.
@ -52,6 +51,6 @@ know you want to!
@teleport #2
#... and drop it (remember, this comment ends input to @teleport, so don't
#forget it!) The very last command in the file needs not be ended with #.
#forget it!) The very last command in the file need not be ended with #.
drop button

View file

@ -0,0 +1,71 @@
#
# Batchcode script
#
#
# The Batch-code processor accepts full python modules (e.g. "batch.py") that
# looks identical to normal Python files with a few exceptions that allows them
# to the executed in blocks. This way of working assures a sequential execution
# of the file and allows for features like stepping from block to block
# (without executing those coming before), as well as automatic deletion
# of created objects etc. You can however also run a batch-code python file
# directly using Python (and can also be de).
# Code blocks are separated by python comments starting with special code words.
# #HEADER - this denotes commands global to the entire file, such as
# import statements and global variables. They will
# automatically be made available for each block. Observe
# that changes to these variables made in one block is not
# preserved between blocks!)
# #CODE [objname, objname, ...] - This designates a code block that will be executed like a
# stand-alone piece of code together with any #HEADER
# defined. <objname>s mark the (variable-)names of objects created in the code,
# and which may be auto-deleted by the processor if desired (such as when
# debugging the script). E.g., if the code contains the command
# myobj = create.create_object(...), you could put 'myobj' in the #CODE header
# regardless of what the created object is actually called in-game.
# The following variables are automatically made available for the script:
# caller - the object executing the script
#
#
#HEADER
# everything in this block will be imported to all CODE blocks when
# they are executed.
from src.utils import create, search
from game.gamesrc.typeclasses.examples import red_button
from game.gamesrc.typeclasses import basetypes
#CODE
# This is the first code block. Within each block, python
# code works as normal.
# get the limbo room.
limbo = search.objects(caller, 'Limbo', global_search=True)[0]
caller.msg(limbo)
# create a red button in limbo
red_button = create.create_object(red_button.RedButton, key="Red button",
location=limbo, aliases=["button"])
# we take a look at what we created
caller.msg("A %s was created." % red_button.key)
#CODE table, chair
# this code block has 'table' and 'chair' set as deletable
# objects. This means that when the batchcode processor runs in
# testing mode, objects created in these variables will be deleted
# again (so as to avoid duplicate objects when testing the script).
limbo = search.objects(caller, 'Limbo', global_search=True)[0]
caller.msg(limbo.key)
table = create.create_object(basetypes.Object, key="Table", location=limbo)
chair = create.create_object(basetypes.Object, key="Chair", location=limbo)
string = "A %s and %s were created. If debug was active, they were deleted again."
caller.msg(string % (table, chair))

View file

@ -1,28 +1,129 @@
#!/usr/bin/env python
"""
Set up the evennia system. A first startup consists of giving
the command './manage syncdb' to setup the system and create
the database.
"""
import sys
import os
# Tack on the root evennia directory to the python path.
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
"""
If settings.py doesn't already exist, create it and populate it with some
basic stuff.
"""
try:
VERSION = open("%s%s%s" % (os.pardir, os.sep, 'VERSION')).readline().strip()
except IOError:
VERSION = "Unknown version"
_CREATED_SETTINGS = False
if not os.path.exists('settings.py'):
print "Can't find a settings.py file, creating one for you."
f = open('settings.py', 'w')
f.write('"""\nMaster server configuration file. You may override any of the values in the\nsrc/config_defaults.py here. Copy-paste the variables here, and make changes to\nthis file rather than editing config_defaults.py directly.\n"""\n')
f.write('from src.config_defaults import *')
f.close()
# If settings.py doesn't already exist, create it and populate it with some
# basic stuff.
settings_file = open('settings.py', 'w')
_CREATED_SETTINGS = True
string = \
"""#
# Evennia MU* server configuration file
#
# You may customize your setup by copy&pasting the variables you want
# to change from the master config file src/settings_default.py to
# this file. Try to *only* copy over things you really need to customize
# and do *not* make any changes to src/settings_default.py directly.
# This way you'll always have a sane default to fall back on
# (also, the master file may change with server updates).
#
from src.settings_default import *
###################################################
# Evennia base server config
###################################################
###################################################
# Evennia Database config
###################################################
###################################################
# Evennia in-game parsers
###################################################
###################################################
# Default command sets
###################################################
###################################################
# Default Object typeclasses
###################################################
###################################################
# Batch processor
###################################################
###################################################
# Game Time setup
###################################################
###################################################
# Game Permissions
###################################################
###################################################
# In-game Channels created from server start
###################################################
###################################################
# IMC2 Configuration
###################################################
###################################################
# IRC config
###################################################
###################################################
# Config for Django web features
###################################################
###################################################
# Evennia components (django apps)
###################################################"""
settings_file.write(string)
settings_file.close()
print """
Welcome to Evennia (version %s)!
Created a fresh settings.py file for you.""" % VERSION
try:
from game import settings
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
except Exception:
import traceback
string = "\n" + traceback.format_exc()
string += """\n
Error: Couldn't import the file 'settings.py' in the directory
containing %r. There can be two reasons for this:
1) You moved your settings.py elsewhere. In that case you need to run
django-admin.py, passing it the true location of your settings module.
2) The settings module is where it's supposed to be, but an exception
was raised when trying to load it. Review the traceback above to
resolve the problem, then try again.
""" % __file__
print string
sys.exit(1)
if __name__ == "__main__":
from django.core.management import execute_manager
execute_manager(settings)
from django.core.management import execute_manager
if _CREATED_SETTINGS:
print """
Edit your new settings.py file as needed, then run
'python manage syncdb' and follow the prompts to
create the database and your superuser account.
"""
sys.exit()
# run the django setups
execute_manager(settings)

View file

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View file

@ -1,5 +0,0 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('game.web.apps.website.views',
(r'^$', 'page_index'),
)

View file

@ -1,9 +0,0 @@
from src.config.models import ConfigValue
def general_context(request):
"""
Returns common Evennia-related context stuff.
"""
return {
'game_name': ConfigValue.objects.get_configvalue('site_name'),
}

View file

@ -0,0 +1,4 @@
The evennia logo (the python snaking a cogwheel-globe) was created in 2009
by Griatch (www.griatch-art.deviantart.com, www.griatch.com) using open-source software (of course).
The logo is released with the same licence as Evennia itself (look in evennia/LICENCE).

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

17
game/web/news/admin.py Normal file
View file

@ -0,0 +1,17 @@
#
# This makes the news model visible in the admin web interface
# so one can add/edit/delete news items etc.
#
from django.contrib import admin
from game.web.news.models import NewsTopic, NewsEntry
class NewsTopicAdmin(admin.ModelAdmin):
list_display = ('name', 'icon')
admin.site.register(NewsTopic, NewsTopicAdmin)
class NewsEntryAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'topic', 'date_posted')
list_filter = ('topic',)
search_fields = ['title']
admin.site.register(NewsEntry, NewsEntryAdmin)

View file

@ -1,5 +1,10 @@
#
# This module implements a simple news entry system
# for the evennia website. One needs to use the
# admin interface to add/edit/delete entries.
#
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class NewsTopic(models.Model):
@ -21,10 +26,6 @@ class NewsTopic(models.Model):
class Meta:
ordering = ['name']
class Admin:
list_display = ('name', 'icon')
admin.site.register(NewsTopic)
class NewsEntry(models.Model):
"""
An individual news entry.
@ -42,8 +43,3 @@ class NewsEntry(models.Model):
ordering = ('-date_posted',)
verbose_name_plural = "News entries"
class Admin:
list_display = ('title', 'author', 'topic', 'date_posted')
list_filter = ('topic',)
search_fields = ['title']
admin.site.register(NewsEntry)

View file

@ -1,6 +1,11 @@
"""
This structures the url tree for the news application.
It is imported from the root handler, game.web.urls.py.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('game.web.apps.news.views',
urlpatterns = patterns('game.web.news.views',
(r'^show/(?P<entry_id>\d+)/$', 'show_news'),
(r'^archive/$', 'news_archive'),
(r'^search/$', 'search_form'),

View file

@ -1,19 +1,20 @@
"""
This is a very simple news application, with most of the expected features
like:
like news-categories/topics and searchable archives.
* News categories/topics
* Searchable archives
"""
import django.views.generic.list_detail as gv_list_detail
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
import django.views.generic.list_detail as gv_list_detail
from django.conf import settings
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django import forms
from django.db.models import Q
from game.web.apps.news.models import NewsTopic, NewsEntry
from game.web.news.models import NewsTopic, NewsEntry
# The sidebar text to be included as a variable on each page. There's got to
# be a better, cleaner way to include this on every page.
@ -89,7 +90,7 @@ def search_form(request):
pagevars = {
"page_title": "Search News",
"search_form": search_form,
"debug": debug,
"debug": settings.DEBUG,
"sidebar": sidebar
}
@ -117,6 +118,7 @@ def search_results(request):
news_entries = NewsEntry.objects.filter(Q(title__contains=cleaned_get['search_terms']) | Q(body__contains=cleaned_get['search_terms']))
pagevars = {
"game_name": settings.SERVERNAME,
"page_title": "Search Results",
"searchtext": cleaned_get['search_terms'],
"browse_url": "/news/search/results",

View file

@ -0,0 +1,11 @@
{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans 'Evennia site admin' %}{% endblock %}
{% block branding %}
<h1 id="site-name">{% trans 'Evennia database administration' %}
<a href="/">(Back)</a> </h1>
{% endblock %}
{% block nav-global %}{% endblock %}

View file

@ -0,0 +1,242 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% load adminmedia %}{% admin_media_prefix %}css/dashboard.css" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %}dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
{% if app.name in evennia_userapps %}
<div class="module">
<table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
<td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endfor %}
<h1>In-game entities</h1>
{% for app in app_list %}
{% if app.name in evennia_entityapps %}
<div class="module">
<table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
<td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endfor %}
<h1>Game setups and configs</h1>
{% for app in app_list %}
{% if app.name in evennia_setupapps %}
<div class="module">
<table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
<td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endfor %}
<h1>Connection protocols</h1>
{% for app in app_list %}
{% if app.name in evennia_connectapps %}
<div class="module">
<table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
<td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endfor %}
<h1>Website Specific</h1>
{% for app in app_list %}
{% if app.name in evennia_websiteapps %}
<div class="module">
<table summary="{% blocktrans with app.name as name %}Models available in the {{ name }} application.{% endblocktrans %}">
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %}
<tr>
{% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.perms.add %}
<td><a href="{{ model.admin_url }}add/" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.perms.change %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endfor %}
{% else %}
<p>{% trans "You don't have permission to edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
{% block sidebar %}
<div id="content-related">
<div class="module" id="recent-actions-module">
<h2>{% trans 'Recent Actions' %}</h2>
<h3>{% trans 'My Actions' %}</h3>
{% load log %}
{% get_admin_log 10 as admin_log for_user user %}
{% if not admin_log %}
<p>{% trans 'None yet.' %}</p>
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
{% if entry.is_deletion %}
{{ entry.object_repr }}
{% else %}
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
{% endif %}
<br/>
{% if entry.content_type %}
<span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span>
{% else %}
<span class="mini quiet">{% trans 'Unknown content' %}</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -29,16 +29,15 @@
<div id="header">
<div class="superHeader">
<span>Related Sites:</span>
<a href="http://evennia.com" title="The Python-based MUD server">Evennia</a> |
<a href="http://www.oswd.org/designs/search/designer/id/3013/" title="Other designs by haran">haran&rsquo;s Designs</a>
<!--span>Sites:</span-->
<a href="http://evennia.com" title="The Python-based MUD server">Evennia.com</a>
</div>
<div class="midHeader">
<h1 class="headerTitle" lang="la">{{game_name}}</h1>
<img src="/media/images/evennia_logo_small.png" align='left'/> <h1 class="headerTitle" lang="la">{{game_name}}</h1>
<div class="headerSubTitle" title="Slogan">
<!-- Insert a slogan here if you want -->
&nbsp;
{{game_slogan}} &nbsp;
</div>
<br class="doNotDisplay doNotPrint" />
@ -79,8 +78,12 @@
<div id="footer">
<span class="doNotPrint">
Template design by
<a href="http://www.oswd.org/designs/search/designer/id/3013/"
title="Other designs by haran">haran</a>.
Powered by
<a href="http://evennia.com">Evennia</a><br />
<a href="http://evennia.com">Evennia.</a>
<br \>
</span>
</div>
</body>

View file

@ -11,12 +11,18 @@
<div class="twoThirds noBorderOnLeft">
<h1>Welcome!</h1>
<p>Welcome to your new installation of Evennia, your friendly
neighborhood next-generation MUD server. You'll want to customize
this file, webtemplates/prosimii/index.html, to have a more
valid welcome message. Should you have any questions, concerns,
ideas, or bug reports, head over to the
<a href="http://evennia.com">Evennia community</a> and
speak up!</p>
neighborhood next-generation MUD server. You are looking at Evennia's web
presence, which can be expanded to a full-fledged site as
needed. Through the <a href="/admin">admin interface</a> you can view and edit the
database without logging into the game. Also take your time to
peruse our extensive online <a href="http://code.google.com/p/evennia/wiki/Index">documentation</a>.
<p>
Should you have any questions, concerns, bug reports, or
if you want to help out, don't hesitate to come join the
<a href="http://evennia.com">Evennia community</a> and get
your voice heard!
</p>
<i>(To edit this file, go to game/web/templates/prosimii/index.html.)</i>
</div>
<div class="oneThird">
@ -47,7 +53,7 @@
<h1>Recently Connected</h1>
<ul>
{% for player in players_connected_recent %}
<li>{{player.username}} -- <em>{{player.last_login|timesince}} ago</em></li>
<li>{{player.user.username}} -- <em>{{player.user.last_login|timesince}} ago</em></li>
{% endfor %}
</ul>
<p class="filler"><!-- Filler para to extend left vertical line --></p>
@ -56,10 +62,9 @@
<div class="quarter">
<h1>Database Stats</h1>
<ul>
<li>{{num_players}} players</li>
<li>{{num_rooms}} rooms</li>
<li>{{num_things}} things</li>
<li>{{num_exits}} exits</li>
<li>{{num_players_registered}} players</li>
<li>{{num_rooms}} rooms ({{num_exits}} exits) </li>
<li>{{num_objects}} objects total</li>
</ul>
</div>
@ -69,9 +74,9 @@
<a href="http://python.org">Python</a>, on top of the
<a href="http://twistedmatrix.com">Twisted</a> and
<a href="http://djangoproject.com">Django</a> frameworks. This
combination of technology allows for the quick and easy creation
of games, as simple as complex as one desires.</p>
combination of technologies allows for the quick and easy creation
of the game of your dreams - as simple or as complex as you like.</p>
</div>
</div>
{% endblock %}
{% endblock %}

View file

@ -4,7 +4,7 @@
{% endblock %}
{% block sidebar %}
{{sidebar}}
{{sidebar|safe}}
{% endblock %}
{% block content %}
@ -48,4 +48,4 @@
{% endif %}
| <a href="{{browse_url}}/?page={{pages}}&search_terms={{searchtext|urlencode}}">Last</a>
{% endblock %}
{% endblock %}

View file

@ -4,7 +4,7 @@
{% endblock %}
{% block sidebar %}
{{sidebar}}
{{sidebar|safe}}
{% endblock %}
{% block content %}
@ -16,4 +16,4 @@
{{search_form.search_terms}}
<button type="Submit">Search</button>
</form>
{% endblock %}
{% endblock %}

View file

@ -4,11 +4,11 @@
{% endblock %}
{% block sidebar %}
{{sidebar}}
{{sidebar|safe}}
{% endblock %}
{% block content %}
<h1 id="alt-layout">{{news_entry.topic.name}}: {{news_entry.title}}</h1>
<p class="newsDate">By {{news_entry.author.username}} on {{news_entry.date_posted|time}}</p>
<p class="newsSummary">{{news_entry.body}}</p>
{% endblock %}
{% endblock %}

View file

@ -13,7 +13,7 @@ Login
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action=".">
<form method="post" action="."{% csrf_token %} >
<table>
<tr>
<td><label for="id_username">Username:</label></td>

View file

@ -10,25 +10,33 @@ from django.conf.urls.defaults import *
from django.conf import settings
from django.contrib import admin
# loop over all settings.INSTALLED_APPS and execute code in
# files named admin.py ine each such app (this will add those
# models to the admin site)
admin.autodiscover()
# Setup the root url tree from /
urlpatterns = patterns('',
# User Authentication
url(r'^accounts/login', 'django.contrib.auth.views.login'),
url(r'^accounts/logout', 'django.contrib.auth.views.logout'),
# Front page
url(r'^', include('game.web.apps.website.urls')),
url(r'^', include('game.web.website.urls')),
# News stuff
url(r'^news/', include('game.web.apps.news.urls')),
url(r'^news/', include('game.web.news.urls')),
# Page place-holder for things that aren't implemented yet.
url(r'^tbi/', 'game.web.apps.website.views.to_be_implemented'),
url(r'^tbi/', 'game.web.website.views.to_be_implemented'),
# Admin interface
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/(.*)', admin.site.root, name='admin'),
url(r'^admin/', include(admin.site.urls)),
#url(r'^admin/(.*)', admin.site.root, name='admin'),
# favicon
url(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url':'/media/images/favicon.ico'}),
)
# If you'd like to serve media files via Django (strongly not recommended!),

View file

View file

@ -1,7 +1,7 @@
import os, sys
# Calculate the path based on the location of the WSGI script.
web_dir = os.path.dirname(__file__)
web_dir = os.path.dirname(os.path.dirname(__file__))
workspace = os.path.dirname(os.path.dirname(web_dir))
sys.path.insert(0, workspace)

View file

@ -0,0 +1,45 @@
# WARNING: mod_python is no longer the recommended way to run Evennia's
# web front end. This file is no longer actively maintained and may
# no longer work. We suggest using mod_wsgi unless absolutely necessary.
# Add this vhost file to your Apache sites-enabled directory, typically found
# at /etc/apache2/sites-enabled. You'll need to go through and change the
# /home/evennia to point to the correct home directory, and the evennia
# subdir must be under that home directory.
# A NOTE ON IMAGES/CSS: These files must be handled separately from the actual
# dynamic content which is interpreted by Python. You may host these static
# images/css from the same server or a different one. The static media
# is served from /home/evennia/evennia/media. The easiest way to serve
# this stuff is by either hosting them remotely or symlinking
# the media directory into an existing Apache site's directory.
# A NOTE ON ADMIN IMAGES/CSS: You'll need to create a symlink called
# 'amedia' under the media directory if you want to use the admin interface.
# This can be found in the Django package in your Python path. For example,
# the default is: /usr/lib/python2.4/site-packages/django/contrib/admin/media/
# although your location may vary.
<VirtualHost *>
# Set ServerName and ServerAdmin appropriately
ServerName evennia.somewhere.com
ServerAdmin someone@somewhere.com
DocumentRoot /home/evennia/evennia
<Directory "/home/evennia/evennia">
SetHandler python-program
PythonHandler django.core.handlers.modpython
PythonPath "['/home/evennia/evennia'] + sys.path"
SetEnv DJANGO_SETTINGS_MODULE settings
PythonDebug On
</Directory>
Alias /media/ "/home/evennia/evennia/media/"
<Directory "/home/evennia/evennia/media/">
SetHandler None
</Directory>
<LocationMatch "\.(jpg|gif|png|css)$">
SetHandler None
</LocationMatch>
</VirtualHost>

View file

@ -0,0 +1,51 @@
<VirtualHost *>
# This is an example mod_wsgi apache2 vhost. There are a number of
# things you'll need to change. They are commented below. Make sure
# you read and understand the stuff below.
# Copy this file to your apache2 vhosts directory before editing.
# This file is merely a template. The vhost directory is usually
# something like /etc/apache2/sites-enabled/.
# You'll want to replace the following with your domain info.
ServerName dev.evennia.com
ServerAlias www.dev.evennia.com
#
## BEGIN EVENNIA WEB CONFIG
#
# evennia_web is just an internal name for mod_wsgi and may be left
# The user and group options are the user and the group that the
# python code will be executed under. This user must have permissions
# to the evennia source.
WSGIDaemonProcess evennia_web user=evennia group=evennia threads=25
WSGIProcessGroup evennia_web
# This needs to be changed to the appropriate path on your django
# install. It serves admin site media.
# For Python 2.6, this is:
# /usr/lib/python2.6/dist-packages/django/contrib/admin/media/
Alias /media/amedia/ "/usr/lib/python2.5/site-packages/django/contrib/admin/media/"
<Directory "/usr/lib/python2.5/site-packages/django/contrib/admin/media">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>
# Media Directory. This needs to be set to your equivalent path.
Alias /media/ "/home/evennia/evennia/game/web/media/"
<Directory "/home/evennia/evennia/game/web/media">
Order allow,deny
Options Indexes FollowSymLinks
Allow from all
IndexOptions FancyIndexing
</Directory>
# WSGI Config File. You'll need to update this path as well.
WSGIScriptAlias / /home/evennia/evennia/game/web/utils/apache_wsgi.conf
#
## END EVENNIA WEB CONFIG
#
</VirtualHost>

View file

@ -0,0 +1,46 @@
#
# This file defines global variables that will always be
# available in a view context without having to repeatedly
# include it. For this to work, this file is included in
# the settings file, in the TEMPLATE_CONTEXT_PROCESSORS
# tuple.
#
from django.db import models
from django.conf import settings
from src.utils.utils import get_evennia_version
# Determine the site name and server version
try:
GAME_NAME = settings.SERVERNAME.strip()
except AttributeError:
GAME_NAME = "Evennia"
SERVER_VERSION = get_evennia_version()
# Setup lists of the most relevant apps so
# the adminsite becomes more readable.
USER_RELATED = ['Auth', 'Players']
GAME_ENTITIES = ['Objects', 'Scripts', 'Comms', 'Help']
GAME_SETUP = ['Permissions', 'Config']
CONNECTIONS = ['Irc', 'Imc2']
WEBSITE = ['Flatpages', 'News', 'Sites']
# The main context processor function
def general_context(request):
"""
Returns common Evennia-related context stuff, which
is automatically added to context of all views.
"""
return {
'game_name': GAME_NAME,
'game_slogan': SERVER_VERSION,
'evennia_userapps': USER_RELATED,
'evennia_entityapps': GAME_ENTITIES,
'evennia_setupapps': GAME_SETUP,
'evennia_connectapps': CONNECTIONS,
'evennia_websiteapps':WEBSITE
}

View file

View file

@ -0,0 +1,7 @@
#
# Define database entities for the app.
#
from django.db import models

10
game/web/website/urls.py Normal file
View file

@ -0,0 +1,10 @@
"""
This structures the (simple) structure of the
webpage 'application'.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('game.web.website.views',
(r'^$', 'page_index'),
)

View file

@ -1,9 +1,13 @@
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.contrib.auth.models import User
from django.conf import settings
from src.objects.models import Object
from game.web.apps.news.models import NewsEntry
from src.config.models import ConfigValue
from src.objects.models import ObjectDB
from src.typeclasses.models import TypedObject
from src.players.models import PlayerDB
from game.web.news.models import NewsEntry
"""
This file contains the generic, assorted views that don't fall under one of
@ -21,23 +25,23 @@ def page_index(request):
# A QuerySet of recent news entries.
news_entries = NewsEntry.objects.all().order_by('-date_posted')[:fpage_news_entries]
# Dictionary containing database statistics.
objstats = Object.objects.object_totals()
# A QuerySet of the most recently connected players.
recent_players = Object.objects.get_recently_connected_users()[:fpage_player_limit]
recent_users = PlayerDB.objects.get_recently_connected_players()[:fpage_player_limit]
exits = ObjectDB.objects.get_objs_with_attr('_destination')
rooms = [room for room in ObjectDB.objects.filter(db_home=None) if room not in exits]
pagevars = {
"page_title": "Front Page",
"news_entries": news_entries,
"players_connected_recent": recent_players,
"num_players_connected": Object.objects.get_connected_players().count(),
"num_players_registered": Object.objects.num_total_players(),
"num_players_connected_recent": Object.objects.get_recently_connected_users().count(),
"num_players_registered_recent": Object.objects.get_recently_created_users().count(),
"num_players": objstats["players"],
"num_rooms": objstats["rooms"],
"num_things": objstats["things"],
"num_exits": objstats["exits"],
"players_connected_recent": recent_users,
"num_players_connected": ConfigValue.objects.conf('nr_sessions'),#len(PlayerDB.objects.get_connected_players()),
"num_players_registered": PlayerDB.objects.num_total_players(),
"num_players_connected_recent": len(PlayerDB.objects.get_recently_connected_players()),
"num_players_registered_recent": len(PlayerDB.objects.get_recently_created_players()),
"num_rooms": len(rooms),
"num_exits": len(exits),
"num_objects" : ObjectDB.objects.all().count()
}
context_instance = RequestContext(request)
@ -55,3 +59,5 @@ def to_be_implemented(request):
context_instance = RequestContext(request)
return render_to_response('tbi.html', pagevars, context_instance)