evennia/docs/sphinx/source/wiki/BatchCodeProcessor.rst

208 lines
9.7 KiB
ReStructuredText

The Batch-Code processor
========================
For an introduction and motivation to using batch processors, see
`here <BatchProcessors.html>`_. This page describes the Batch-*code*
processor. The Batch-*command* one is covered
`here <BatchCommandProcessor.html>`_.
Basic Usage
-----------
The batch-command processor is a superuser-only function, invoked by
::
> @batchcode path.to.batchcodefile
Where ``path.to.batchcodefile`` is the path to a *batch-code file* with
the "``.py``" file ending. This path is given like a python path
relative to a folder you define to hold your batch files, set by
``BATCH_IMPORT_PATH`` in your settings. Default folder is
``game/gamesrc/world``. So if you want to run the example batch file in
``game/gamesrc/world/examples/batch_code.py``, you could simply use
::
> @batchcommand examples.batch_code
This will try to run through the entire batch file in one go. For more
gradual, *interactive* control you can use the ``/interactive`` switch.
The switch ``/debug`` will put the processor in *debug* mode. Read below
for more info.
The batch file
--------------
A batch-code file is mostly a normal Python source file. The only thing
separating a batch file from any Python module is that the code are
wrapped into *blocks* using a special syntax. These blocks allow the
batch processor more control over execution, especially when using the
processor's *interactive* mode, where they allow the default
``@batchcommand`` to pause and only execute certain blocks at a time.
Here are the rules of syntax of the batch-command ``*.py`` file.
- ``#HEADER`` as the first on a line marks the start of a *header*
block. This is intended to hold imports and variables that might be
of use for for other blocks. All python code defined in a header
block will always be inserted at the top of all ``#CODE`` blocks in
the file. You may have more than one ``#HEADER`` block, but that is
equivalent to having just one big one. Comments in ``#HEADER`` blocks
are stripped out before merging.
- ``#CODE`` as the first on a line marks the start of a *code* block.
Code blocks contain functional python code. ``#HEADER`` blocks are
added to the top of code blocks at runtime.
- ``#CODE (info) obj1, obj2, ...`` is an optional form of the code
block header. The ``(info)`` field gives extra info about what's
going on in the block and is displayed by the batch processor. The
``obj1, obj2, ...`` parts are optional object labels used by the
processors *debug* mode in order to auto-delete objects after a test
run.
- A new ``#HEADER`` or ``#CODE`` (or the end of the file) ends the
previous block. Text before the first block are ignored.
- A ``#`` that is not starting a ``#HEADER`` or ``#CODE`` block is
considered a comment.
- Inside a block, normal Python syntax rules apply. For the sake of
indentation, each block acts as a separate python module.
- The variable ``caller`` is always made available to the script,
pointing to the object executing the batchcommand.
Below is a version of the example file found in
``game/gamesrc/commands/examples/batch_code.py``.
::
#
# This is an example batch-code build file for Evennia.
##HEADER# This will be included in all other #CODE blocksfrom src.utils import create, search
from game.gamesrc.objects.examples import red_button
from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]#CODE (create red button)red_button = create.create_object(red_button.RedButton, key="Red button",
location=limbo, aliases=["button"])# caller points to the one running the script
caller.msg("A red button was created.")#CODE (create table and chair) table, chairtable = create.create_object(baseobjects.Object, key="Blue Table", location=limbo)
chair = create.create_object(baseobjects.Object, key="Blue Chair", location=limbo)string = "A %s and %s were created. If debug was active, they were deleted again."
caller.msg(string % (table, chair))
This uses Evennia's Python API to create three objects in sequence.
Debug mode
----------
Try to run the example script with
::
> @batchcode/debug examples.batch_code
The batch script will run to the end and tell you it completed. You will
also get messages that the button and the two pieces of furniture where
created. Look around and you should see the button there. But you won't
see any chair nor a table! This is because we ran this with the
``/debug`` switch. The debug mode of the processor is intended to be
used when you test out a script. Maybe you are looking for bugs in your
code or try to see if things behave as they should. Running the script
over and over would then create an ever-growing stack of buttons, chairs
and tables, all with the same name. You would have to go back and
painstakingly delete them later. The debug mode simply tries to
automatically delete the objects that where created so as to not crowd
the room with unwanted objects.
The second ``#CODE`` block supplies the variable names ``table`` and
``chair``, which match the actual variables we later assign our new
ojects to. In debug mode the batch-code processor will look for these
references and simply run ``delete()`` on them. Since the
button-creating block does not define any such variables the processor
can't help us there - meaning the button stays also in debug mode.
Interactive mode
----------------
Interactive mode works very similar to the `batch-command processor
counterpart <BatchCommandProcessor.html>`_. It allows you more step-wise
control over how the batch file is executed. This is useful for
debugging or for picking and choosing only particular blocks to run. Use
``@batchcommand`` with the ``/interactive`` flag to enter interactive
mode.
::
> @batchcode/interactive examples.batch_code
You should see the following:
::
01/02: #CODE (create red button) [...] (hh for help)
This shows that you are on the first ``#CODE`` block, the first of only
two commands in this batch file. Observe that the block has *not*
actually been executed at this point!
To take a look at the full command you are about to run, use ``ll`` (a
batch-processor version of ``look``).
::
from src.utils import create, search
from game.gamesrc.objects.examples import red_button
from game.gamesrc.objects import baseobjectslimbo = search.objects(caller, 'Limbo', global_search=True)[0]red_button = create.create_object(red_button.RedButton, key="Red button",
location=limbo, aliases=["button"])# caller points to the one running the script
caller.msg("A red button was created.")
Compare with the example code given earlier. Notice how the content of
``#HEADER`` has been pasted at the top of the ``#CODE`` block. Use
``pp`` to actually execute this block (this will create the button and
give you a message). Use ``nn`` (next) to go to the next command. Use
``hh`` for a list of commands.
If there are tracebacks, fix them in the batch file, then use ``rr`` to
reload the file. You will still be at the same code block and can rerun
it easily with ``pp`` as needed. This makes for a simple debug cycle. It
also allows you to rerun individual troublesome blocks - as mentioned,
in a large batch file this can be very useful (don't forget the
``/debug`` mode either).
Use ``nn`` and ``bb`` (next and back) to step through the file; e.g.
``nn 12`` will jump 12 steps forward (without processing any blocks in
between). All normal commands of Evennia should work too while working
in interactive mode.
Limitations and Caveats
-----------------------
The batch-code processor is by far the most flexible way to build a
world in Evennia. There are however some caveats you need to keep in
mind.
- *Safety*. Or rather the lack of it. There is a reason only
*superusers* are allowed to run the batch-code processor by default.
The code-processor runs *without any Evennia security checks* and
allows full access to Python. If an untrusted party could run the
code-processor they could execute arbitrary python code on your
machine, which is potentially a very dangerous thing. If you want to
allow other users to access the batch-code processor you should make
sure to run Evennia as a separate and very limited-access user on
your machine (i.e. in a 'jail'). By comparison, the batch-command
processor is much safer since the user running it is still 'inside'
the game and can't really do anything outside what the game commands
allow them to.
- *You cannot communicate between code blocks*. Global variables won't
work in code batch files, each block is executed as stand-alone
environments. Similarly you cannot in one ``#CODE`` block assign to
variables from the ``#HEADER`` block and expect to be able to read
the changes from another ``#CODE`` block (whereas a python execution
limitation, allowing this would also lead to very hard-to-debug code
when using the interactive mode). The main issue with this is when
building e.g. a room in one code block and later want to connect that
room with a room you built in another block. To do this, you must
perform a database search for the name of the room you created (since
you cannot know in advance which dbref it got assigned). This sounds
iffy, but there is an easy way to handler this - use object aliases.
You can assign any number of aliases to any object. Make sure that
one of those aliases is unique (like "room56") and you will
henceforth be able to always find it later by searching for it from
other code blocks regardless of if the main name is shared with
hundreds of other rooms in your world (coincidentally, this is also
one way of implementing "zones", should you want to group rooms
together).