Cleaned up the batch processors (both cmd- and code processor) and homogenized their interfaces and APIs. Also test-ran the example codes and fixed some bugs.

This commit is contained in:
Griatch 2010-09-02 11:39:01 +00:00
parent e114c33d8a
commit 4d8fc05157
6 changed files with 373 additions and 316 deletions

View file

@ -23,23 +23,46 @@ 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 src.utils.batchprocessors import BATCHCMD, BATCHCODE
from game.gamesrc.commands.default.muxcommand import MuxCommand
from src.commands.cmdset import CmdSet
HEADER_WIDTH = 70
#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 format_header(caller, entry):
"""
Formats a header
"""
width = HEADER_WIDTH - 10
entry = entry.strip()
header = entry[:min(width, min(len(entry), entry.find('\n')))]
if len(entry) > width:
header = "%s[...]" % header
ptr = caller.ndb.batch_stackptr + 1
stacklen = len(caller.ndb.batch_stack)
header = "{w%02i/%02i{G: %s{n" % (ptr, stacklen, header)
# add extra space to the side for padding.
header = "%s%s" % (header, " "*(width-len(header)))
header = header.replace('\n', '\\n')
return header
def format_code(entry):
"""
Formats the viewing of code and errors
"""
code = ""
for line in entry.split('\n'):
code += "\n{G>>>{n %s" % line
return code.strip()
def batch_cmd_exec(caller):
"""
Helper function for executing a single batch-command entry
@ -47,19 +70,14 @@ def batch_cmd_exec(caller):
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))))
caller.msg(format_header(caller, command))
try:
caller.execute_cmd(command)
except Exception:
caller.msg(format_exc())
caller.msg(format_code(format_exc()))
return False
return True
def batch_code_exec(caller):
"""
Helper function for executing a single batch-code entry
@ -67,20 +85,16 @@ def batch_code_exec(caller):
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)
caller.msg(format_header(caller, codedict['code']))
err = BATCHCODE.code_exec(codedict,
extra_environ={"caller":caller}, debug=debug)
if err:
caller.msg(err)
caller.msg(format_code(err))
return False
return True
def step_pointer(caller, step=1):
"""
Step in stack, returning the item located.
@ -93,13 +107,15 @@ def step_pointer(caller, step=1):
stack = caller.ndb.batch_stack
nstack = len(stack)
if ptr + step <= 0:
caller.msg("Beginning of batch file.")
caller.msg("{RBeginning of batch file.")
if ptr + step >= nstack:
caller.msg("End of batch file.")
caller.msg("{REnd 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."
"""
Show the current position in stack
"""
stackptr = caller.ndb.batch_stackptr
stack = caller.ndb.batch_stack
@ -107,25 +123,39 @@ def show_curr(caller, showall=False):
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']
# this is a batch-code entry
string = format_header(caller, entry['code'])
codeall = entry['code'].strip()
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)
# this is a batch-cmd entry
string = format_header(caller, entry)
codeall = entry.strip()
string += "{G(hh for help)"
if showall:
string += "\n%s" % codeall
for line in codeall.split('\n'):
string += "\n{n>>> %s" % line
caller.msg(string)
def purge_processor(caller):
"""
This purges all effects running
on the caller.
"""
try:
del caller.ndb.batch_stack
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
except:
pass
# clear everything but the default cmdset.
caller.cmdset.delete(BatchSafeCmdSet)
caller.cmdset.clear()
caller.scripts.validate() # this will purge interactive mode
#------------------------------------------------------------
# main access commands
@ -164,7 +194,7 @@ class CmdBatchCommands(MuxCommand):
#parse indata file
commands = batchprocessors.parse_batchcommand_file(python_path)
commands = BATCHCMD.parse_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)."
@ -202,7 +232,8 @@ class CmdBatchCommands(MuxCommand):
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))))
caller.msg("{G%s" % string)
purge_processor(caller)
class CmdBatchCode(MuxCommand):
"""
@ -240,7 +271,7 @@ class CmdBatchCode(MuxCommand):
python_path = self.args
#parse indata file
codes = batchprocessors.parse_batchcode_file(python_path)
codes = BATCHCODE.parse_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)."
@ -283,7 +314,8 @@ class CmdBatchCode(MuxCommand):
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))))
caller.msg("{G%s" % string)
purge_processor(caller)
#------------------------------------------------------------
# State-commands for the interactive batch processor modes
@ -294,23 +326,17 @@ class CmdStateAbort(MuxCommand):
"""
@abort
Exits back the default cmdset, regardless of what state
we are currently in.
This is a safety feature. It force-ejects us out of the processor and to
the default cmdset, regardless of what current cmdset the processor might
have put us in (e.g. when testing buggy scripts etc).
"""
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.")
purge_processor(self.caller)
self.caller.msg("Exited processor and reset out active cmdset back to the default one.")
class CmdStateLL(MuxCommand):
"""
@ -358,10 +384,10 @@ class CmdStateRR(MuxCommand):
def func(self):
caller = self.caller
if caller.ndb.batch_batchmode == "batch_code":
batchprocessors.read_batchcommand_file(caller.ndb.batch_pythonpath)
BATCHCODE.read_file(caller.ndb.batch_pythonpath)
else:
batchprocessors.read_batchcode_file(caller.ndb.batch_pythonpath)
caller.msg("\nFile reloaded. Staying on same command.\n")
BATHCMD.read_file(caller.ndb.batch_pythonpath)
caller.msg(format_code("File reloaded. Staying on same command."))
show_curr(caller)
class CmdStateRRR(MuxCommand):
@ -377,11 +403,11 @@ class CmdStateRRR(MuxCommand):
def func(self):
caller = self.caller
if caller.ndb.batch_batchmode == "batch_code":
batchprocessors.read_batchcommand_file(caller.ndb.batch_pythonpath)
BATCHCODE.read_file(caller.ndb.batch_pythonpath)
else:
batchprocessors.read_batchcode_file(caller.ndb.batch_pythonpath)
BATCHCMD.read_file(caller.ndb.batch_pythonpath)
caller.ndb.batch_stackptr = 0
caller.msg("\nFile reloaded. Restarting from top.\n")
caller.msg(format_code("File reloaded. Restarting from top."))
show_curr(caller)
class CmdStateNN(MuxCommand):
@ -545,7 +571,7 @@ class CmdStateCC(MuxCommand):
del caller.ndb.batch_stackptr
del caller.ndb.batch_pythonpath
del caller.ndb.batch_batchmode
caller.msg("Finished processing batch file.")
caller.msg(format_code("Finished processing batch file."))
class CmdStateJJ(MuxCommand):
"""
@ -562,7 +588,7 @@ class CmdStateJJ(MuxCommand):
if arg and arg.isdigit():
number = int(self.args)-1
else:
caller.msg("You must give a number index.")
caller.msg(format_code("You must give a number index."))
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
@ -584,7 +610,7 @@ class CmdStateJL(MuxCommand):
if arg and arg.isdigit():
number = int(self.args)-1
else:
caller.msg("You must give a number index.")
caller.msg(format_code("You must give a number index."))
return
ptr = caller.ndb.batch_stackptr
step = number - ptr
@ -601,26 +627,19 @@ class CmdStateQQ(MuxCommand):
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.")
purge_processor(self.caller)
self.caller.msg("Aborted interactive batch mode.")
class CmdStateHH(MuxCommand):
"Help command"
key = "help"
aliases = "hh"
key = "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)
@ -637,6 +656,11 @@ class CmdStateHH(MuxCommand):
cc - continue processing to end, then quit.
qq - quit (abort all remaining commands)
@abort - this is a safety command that always is available
regardless of what cmdsets gets added to us during
batch-command processing. It immediately shuts down
the processor and returns us to the default cmdset.
"""
self.caller.msg(string)

View file

@ -603,13 +603,13 @@ class CmdCreate(ObjManipCommand):
obj = create.create_object(typeclass, name, caller,
home=caller, aliases=aliases)
if not obj:
string += "\nError when creating object."
string = "Error when creating object."
continue
if aliases:
string += "\nYou create a new %s: %s (aliases: %s)."
string = "You create a new %s: %s (aliases: %s)."
string = string % (obj.typeclass, obj.name, ", ".join(aliases))
else:
string += "\nYou create a new %s: %s."
string = "You create a new %s: %s."
string = string % (obj.typeclass, obj.name)
if 'drop' in self.switches:
if caller.location:
@ -1189,21 +1189,22 @@ class CmdDestroy(MuxCommand):
for objname in self.lhslist:
obj = caller.search(objname)
if not obj:
continue
continue
objname = obj.name
if obj.player and not 'override' in self.switches:
string += "\n\rObject %s is a player object. Use /override to delete anyway." % objname
string = "Object %s is a player object. Use /override to delete anyway." % objname
continue
if not has_perm(caller, obj, 'create'):
string += "\n\rYou don't have permission to delete %s." % objname
string = "You don't have permission to delete %s." % objname
continue
# do the deletion
okay = obj.delete()
if not okay:
string += "\n\rERROR: %s NOT deleted, probably because at_obj_delete() returned False." % objname
string = "ERROR: %s NOT deleted, probably because at_obj_delete() returned False." % objname
else:
string += "\n\r%s was deleted." % objname
caller.msg(string.strip('\n'))
string = "%s was deleted." % objname
if string:
caller.msg(string.strip())
#NOT VALID IN NEW SYSTEM!

View file

@ -17,37 +17,43 @@
# 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
# #CODE (infotext) [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,
# defined.
# infotext is a describing text about what goes in in this block. It will be
# shown by the batchprocessing command.
# <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:
# The following variable is 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.
# everything in this block will be appended to the beginning of
# all other #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
from game.gamesrc.objects.examples import red_button
from game.gamesrc.objects import baseobjects
#CODE
limbo = search.objects(caller, 'Limbo', global_search=True)[0]
#CODE (create red button)
# This is the first code block. Within each block, python
# code works as normal.
# code works as normal. Note how we make use if imports and
# 'limbo' defined in the #HEADER block. This block's header
# offers no information about red_button variable, so it
# won't be able to be deleted in debug mode.
# 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"])
@ -55,17 +61,22 @@ red_button = create.create_object(red_button.RedButton, key="Red button",
# we take a look at what we created
caller.msg("A %s was created." % red_button.key)
#CODE table, chair
#CODE (create table and chair) 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).
# again (so as to avoid duplicate objects when testing the script many
# times).
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)
# the python variables we assign to must match the ones given in the
# header for the system to be able to delete them afterwards during a
# debugging run.
table = create.create_object(baseobjects.Object, key="Table", location=limbo)
chair = create.create_object(baseobjects.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))