Trunk: Merged the Devel-branch (branches/griatch) into /trunk. This constitutes a major refactoring of Evennia. Development will now continue in trunk. See the wiki and the past posts to the mailing list for info. /Griatch
This commit is contained in:
parent
df29defbcd
commit
f83c2bddf8
222 changed files with 22304 additions and 14371 deletions
File diff suppressed because one or more lines are too long
112
game/evennia.py
112
game/evennia.py
|
|
@ -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')
|
||||
|
|
|
|||
78
game/gamesrc/commands/basecommand.py
Normal file
78
game/gamesrc/commands/basecommand.py
Normal 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)
|
||||
689
game/gamesrc/commands/default/batchprocess.py
Normal file
689
game/gamesrc/commands/default/batchprocess.py
Normal 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())
|
||||
98
game/gamesrc/commands/default/cmdset_default.py
Normal file
98
game/gamesrc/commands/default/cmdset_default.py
Normal 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())
|
||||
|
||||
21
game/gamesrc/commands/default/cmdset_unloggedin.py
Normal file
21
game/gamesrc/commands/default/cmdset_unloggedin.py
Normal 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())
|
||||
792
game/gamesrc/commands/default/comms.py
Normal file
792
game/gamesrc/commands/default/comms.py
Normal 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))
|
||||
|
||||
703
game/gamesrc/commands/default/general.py
Normal file
703
game/gamesrc/commands/default/general.py
Normal 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)
|
||||
281
game/gamesrc/commands/default/help.py
Normal file
281
game/gamesrc/commands/default/help.py
Normal 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)
|
||||
204
game/gamesrc/commands/default/info.py
Normal file
204
game/gamesrc/commands/default/info.py
Normal 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)
|
||||
147
game/gamesrc/commands/default/muxcommand.py
Normal file
147
game/gamesrc/commands/default/muxcommand.py
Normal 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)
|
||||
1685
game/gamesrc/commands/default/objmanip.py
Normal file
1685
game/gamesrc/commands/default/objmanip.py
Normal file
File diff suppressed because it is too large
Load diff
670
game/gamesrc/commands/default/privileged.py
Normal file
670
game/gamesrc/commands/default/privileged.py
Normal 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))
|
||||
|
||||
173
game/gamesrc/commands/default/syscommands.py
Normal file
173
game/gamesrc/commands/default/syscommands.py
Normal 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")
|
||||
190
game/gamesrc/commands/default/tests.py
Normal file
190
game/gamesrc/commands/default/tests.py
Normal 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")
|
||||
211
game/gamesrc/commands/default/unimplemented/imc2.py
Normal file
211
game/gamesrc/commands/default/unimplemented/imc2.py
Normal 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")
|
||||
|
||||
130
game/gamesrc/commands/default/unimplemented/irc.py
Normal file
130
game/gamesrc/commands/default/unimplemented/irc.py
Normal 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")
|
||||
235
game/gamesrc/commands/default/unimplemented/search.py
Normal file
235
game/gamesrc/commands/default/unimplemented/search.py
Normal 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")
|
||||
295
game/gamesrc/commands/default/unloggedin.py
Normal file
295
game/gamesrc/commands/default/unloggedin.py
Normal 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)
|
||||
292
game/gamesrc/commands/examples/cmdset_red_button.py
Normal file
292
game/gamesrc/commands/examples/cmdset_red_button.py
Normal 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())
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
158
game/gamesrc/objects/baseobjects.py
Normal file
158
game/gamesrc/objects/baseobjects.py
Normal 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
|
||||
168
game/gamesrc/objects/examples/red_button.py
Normal file
168
game/gamesrc/objects/examples/red_button.py
Normal 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)
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
63
game/gamesrc/scripts/basescript.py
Normal file
63
game/gamesrc/scripts/basescript.py
Normal 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
|
||||
0
game/web/apps/news/__init__.py → game/gamesrc/scripts/examples/__init__.py
Executable file → Normal file
0
game/web/apps/news/__init__.py → game/gamesrc/scripts/examples/__init__.py
Executable file → Normal file
275
game/gamesrc/scripts/examples/red_button_scripts.py
Normal file
275
game/gamesrc/scripts/examples/red_button_scripts.py
Normal 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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
71
game/gamesrc/world/examples/batch_code.py
Normal file
71
game/gamesrc/world/examples/batch_code.py
Normal 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))
|
||||
133
game/manage.py
133
game/manage.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns('game.web.apps.website.views',
|
||||
(r'^$', 'page_index'),
|
||||
)
|
||||
|
|
@ -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'),
|
||||
}
|
||||
4
game/web/media/images/LICENCE
Normal file
4
game/web/media/images/LICENCE
Normal 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).
|
||||
BIN
game/web/media/images/evennia_logo.png
Executable file
BIN
game/web/media/images/evennia_logo.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 672 KiB |
BIN
game/web/media/images/evennia_logo_small.png
Executable file
BIN
game/web/media/images/evennia_logo_small.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
game/web/media/images/favicon.ico
Normal file
BIN
game/web/media/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
0
game/web/apps/website/__init__.py → game/web/news/__init__.py
Normal file → Executable file
0
game/web/apps/website/__init__.py → game/web/news/__init__.py
Normal file → Executable file
17
game/web/news/admin.py
Normal file
17
game/web/news/admin.py
Normal 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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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'),
|
||||
|
|
@ -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",
|
||||
11
game/web/templates/admin/base_site.html
Normal file
11
game/web/templates/admin/base_site.html
Normal 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 %}
|
||||
242
game/web/templates/admin/index.html
Normal file
242
game/web/templates/admin/index.html
Normal 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> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.perms.change %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </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> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.perms.change %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </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> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.perms.change %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </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> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.perms.change %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </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> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.perms.change %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
{% else %}
|
||||
<td> </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 %}
|
||||
|
|
@ -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’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 -->
|
||||
|
||||
{{game_slogan}}
|
||||
</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>
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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 %}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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!),
|
||||
|
|
|
|||
0
game/web/utils/__init__.py
Normal file
0
game/web/utils/__init__.py
Normal 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)
|
||||
45
game/web/utils/evennia_modpy_apache.conf
Normal file
45
game/web/utils/evennia_modpy_apache.conf
Normal 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>
|
||||
51
game/web/utils/evennia_wsgi_apache.conf
Normal file
51
game/web/utils/evennia_wsgi_apache.conf
Normal 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>
|
||||
46
game/web/utils/general_context.py
Normal file
46
game/web/utils/general_context.py
Normal 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
|
||||
}
|
||||
0
game/web/website/__init__.py
Normal file
0
game/web/website/__init__.py
Normal file
7
game/web/website/models.py
Normal file
7
game/web/website/models.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Define database entities for the app.
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
10
game/web/website/urls.py
Normal file
10
game/web/website/urls.py
Normal 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'),
|
||||
)
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue