evennia/docs/sphinx/source/wiki/CodingUtils.rst
2013-05-15 23:49:38 +02:00

256 lines
9.1 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Utils
=====
Evennia comes with many utilities to help with common coding tasks. Some
of these are part of the command interface but most can be found in the
``src/utils/`` folder. They are used all over the server, but offer help
for many common tasks when coding your own game as well. This is not a
complete list, check the module for more goodies.
Search
------
A common thing to do is to search for objects. The most common time one
needs to do this is inside a command body. There it's easiest to use the
``search`` method defined on all objects. This will search for objects
in the same location and inside the caller:
::
obj = self.caller.search(objname)
Give the keyword ``global_search=True`` to extend search to encompass
entire database. Also aliases with be matched by this search. You will
find multiple examples of this functionality in the default command set.
If you need to search for objects in a code module you can use the
functions in ``src.utils.search``. You can access these as shortcuts
``ev.search_*``.
::
from ev import search_object
obj = search_object(objname)
``utils.search`` contains properties to the relevant database search
method. They are really just shortcuts to the django-database search
methods, like ``ObjectDB.objects.search()``.
**Note:** If you are a Django wizz, you might be tempted to use Django's
database search functions directly (using ``filter``, ``get`` etc). Just
remember that such operations will give you a django model whereas
Evennia's manager methods will give you a typeclass. It's easy to
convert between them with ``dbobj.typeclass`` and ´typeclass.dbobj´,
but you should remember this distinction. If you stick with Evennia's
search methods you will always get typeclasses back.
Create
------
Apart from the in-game build commands (``@create`` etc), you can also
build all of Evennia's game entities directly in code (for example when
defining new create commands). This *must* be done using
``src.utils.create`` or their shortcuts ``ev.create_*``- these functions
are responsible for setting up all the background intricacies of the
typeclass system and other things. Creating database instances using raw
Django will *not* work. Examples:
::
import ev
#
myobj = ev.create_objects("game.gamesrc.objects.myobj.MyObj", key="MyObj")
myscr = ev.create_script("game.gamesrc.scripts.myscripts.MyScript", obj=myobj)
help = ev.create_help_entry("Emoting", "Emoting means that ...")
msg = ev.create_message(senderobj, [receiverobj], "Hello ...")
chan = ev.create_channel("news")
player = ev.create_player("Henry", "henry@test.com", "H@passwd")
Each of these create functions have a host of arguments to further
customize the created entity. See ``src/utils/create.py`` for more
information.
Logging
-------
Normally you can use Python ``print`` statements to see output to the
terminal (if you started Evennia in *interactive mode* with the -i
switch). This should only be used for debugging though, for real output,
use the logger - it will log to the terminal in interactive mode, to the
log file otherwise.
::
from ev import logger
#
logger.log_errmsg("This is an Error!")
logger.log_warnmsg("This is a Warning!")
logger.log_infomsg("This is normal information")
logger.log_depmsg("This feature is deprecated")
There is also a special log-message type that is intended to be called
from inside a traceback - this can be very useful for relaying the
traceback message back to log without having it kill the server.
::
try:
# [some code that may fail...]
except Exception:
logger.log_trace("This text will be appended to the traceback info")
inherits\_from()
----------------
This useful function takes two arguments - an object to check and a
parent. It returns ``True`` if object inherits from parent *at any
distance* (as opposed to Python's in-built ``is_instance()`` that will
only catch immediate dependence). This function also accepts as input
any combination of classes, instances or python-paths-to-classes.
Note that Python code should usually work with `duck
typing <http://en.wikipedia.org/wiki/Duck_typing>`_. But in Evennia's
case it can sometimes be useful to check if an object inherits from a
given `Typeclass <Typeclasses.html>`_ as a way of identification. Say
for example that we have a typeclass *Animal*. This has a subclass
*Felines* which in turns is a parent to *HouseCat*. Maybe there are a
bunch of other animal types too, like horses and dogs. Using
``inherits_from`` will allow you to check for all animals in one go:
::
from ev import utils
if (utils.inherits_from(obj, "game.gamesrc.objects.animals.Animal"):
obj.msg("The bouncer stops you in the door. He says: 'No talking animals allowed.'")
delay()
-------
This is a thin wrapper around a Twisted construct called a *deferred*.
It simply won't return until a given number of seconds have passed, at
which time it will trigger a given callback with whatever argument. This
is a small and lightweight (non-persistent) alternative to a full
`Script <Scripts.html>`_. Contrary to a Script it can also handle
sub-second timing precision (although this is not something you should
normally need to worry about).
Some text utilities
-------------------
In a text game, you are naturally doing a lot of work shuffling text
back and forth. Here is a *non-complete* selection of text utilities
found in ``src/utils/utils.py`` (shortcut ``ev.utils``). If nothing else
it can be good to look here before starting to develop a solution of
your own.
fill()
~~~~~~
This flood-fills a text to a given width (shuffles the words to make
each line evenly wide). It also indents as needed.
::
outtxt = fill(intxt, width=78, indent=4)
crop()
~~~~~~
This function will crop a very long line, adding a suffix to show the
line actually continues. This can be useful in listings when showing
multiple lines would mess up things.
::
intxt = "This is a long text that we want to crop."
outtxt = crop(intxt, width=19, suffix="[...]")
# outtxt is now "This is a long text[...]"
dedent()
~~~~~~~~
This solves what may at first glance appear to be a trivial problem with
text - removing indentations. It is used to shift entire paragraphs to
the left, without disturbing any further formatting they may have. A
common case for this is when using Python triple-quoted strings in code
- they will retain whichever indentation they have in the code, and to
make easily-readable source code one usually don't want to shift the
string to the left edge.
::
#python code is entered at a given indentation
intxt = """
This is an example text that will end
up with a lot of whitespace on the left.
It also has indentations of
its own."""
outtxt = dedent(intxt)
# outtxt will now retain all internal indentation
# but be shifted all the way to the left.
Normally you do the dedent in the display code (this is for example how
the help system homogenizes help entries).
time\_format()
~~~~~~~~~~~~~~
This function takes a number of seconds as input and converts it to a
nice text output in days, hours etc. It's useful when you want to show
how old something is. It converts to four different styles of output
using the *style* keyword:
- style 0 - ``5d:45m:12s`` (standard colon output)
- style 1 - ``5d`` (shows only the longest time unit)
- style 2 - ``5 days, 45 minutes`` (full format, ignores seconds)
- style 3 - ``5 days, 45 minutes, 12 seconds`` (full format, with
seconds)
text conversion()
~~~~~~~~~~~~~~~~~
Evennia supplies two utility functions for converting text to the
correct encodings. ``to_str()`` and ``to_unicode()``. The difference
from Python's in-built ``str()`` and ``unicode()`` operators are that
the Evennia ones makes use of the ``ENCODINGS`` setting and will try
very hard to never raise a traceback but instead echo errors through
logging. See `TextEncodings <TextEncodings.html>`_ for more info.
format\_table()
~~~~~~~~~~~~~~~
This function creates nicely formatted tables - columns of text all
lined up. It will automatically widen each column so all entries fit.
To use it, you need to create a list of lists - each sublist contains
the content of one column. The result will be a list of ready-formatted
strings to print.
::
# title line
cols = [["num"],["x"],["y"]]
# creating a dummy table with integers
for i in range(3):
cols[0].append(i)
cols[1].append(i+1)
cols[2].append(i+2)
# format the table (returns list with rows)
ftable = format_table(cols, extra_space=3)
# print the rows, making header bright white
for irow, row in enumerate(ftable):
if irow == 0: # header
print "{w%s{x" % row
else:
print row
# Output (no colors shown):
#
# num x y
# 1 2 3
# 2 3 4
# 3 4 5
#
Note that you cannot add colour codes to the input to ``format_table`` -
these would mess up the width of each column. Instead you can add this
to the output when printing.