Evennia comes with many utilities to help with common coding tasks. Most
are accessible directly from the flat API, otherwise you can find them
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. Aliases will also 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
evennia.utils.search. You can access these as shortcuts
from evennia import search_object obj = search_object(objname)
Note that these latter methods will always return a
list of results,
even if the list has one or zero entries.
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).
import evennia # myobj = evennia.create_objects("game.gamesrc.objects.myobj.MyObj", key="MyObj") myscr = evennia.create_script("game.gamesrc.scripts.myscripts.MyScript", obj=myobj) helpentry = evennia.create_help_entry("Emoting", "Emoting means that ...") msg = evennia.create_message(senderobj, [receiverobj], "Hello ...") channel = evennia.create_channel("news") account = evennia.create_account("Henry", "firstname.lastname@example.org", "H@passwd")
Each of these create-functions have a host of arguments to further
customize the created entity. See
evennia/utils/create.py for more
Normally you can use Python
logger which will create
proper logs either to terminal or to file.
from evennia import logger # logger.log_err("This is an Error!") logger.log_warn("This is a Warning!") logger.log_info("This is normal information") logger.log_dep("This feature is deprecated")
There is a special log-message type,
log_trace() 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 show beneath the traceback itself.")
log_file logger, finally, is a very useful logger for outputting
arbitrary log messages. This is a heavily optimized asynchronous log
mechanism using threads to avoid overhead. You should be able to use
it for very heavy custom logging without fearing disk-write delays.
If not an absolute path is given, the log file will appear in the
mygame/server/logs/ directory. If the file already exists, it will
be appended to. Timestamps on the same format as the normal Evennia logs
will be automatically added to each entry. If a filename is not
specified, output will be written to a file
Evennia tracks the current server time. You can access this time via the
from evennia import gametime # all the functions below return times in seconds). # total running time of the server runtime = gametime.runtime() # time since latest hard reboot (not including reloads) uptime = gametime.uptime() # server epoch (its start time) server_epoch = gametime.server_epoch() # in-game epoch (this can be set by `settings.TIME_GAME_EPOCH`. # If not, the server epoch is used. game_epoch = gametime.game_epoch() # in-game time passed since time started running gametime = gametime.gametime() # in-game time plus game epoch (i.e. the current in-game # time stamp) gametime = gametime.gametime(absolute=True) # reset the game time (back to game epoch) gametime.reset_gametime()
TIME_FACTOR determines how fast/slow in-game time runs
compared to the real world. The setting
TIME_GAME_EPOCH sets the
starting game epoch (in seconds). The functions from the
module all return their times in seconds. You can convert this to
whatever units of time you desire for your game. You can use the
@time command to view the server time info.
You can also schedule things to happen at specific in-game times using the gametime.schedule function:
import evennia def church_clock: limbo = evennia.search_object(key="Limbo") limbo.msg_contents("The church clock chimes two.") gametime.schedule(church_clock, hour=2)
This function takes a number of seconds as input (e.g. from the
gametime module above) 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
- 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)
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. But in
Evennia’s case it can sometimes be useful to check if an object inherits
from a given Typeclass as a way of identification. Say for example
that we have a typeclass Animal. This has a subclass Felines which
in turn has a subclass HouseCat. Maybe there are a bunch of other
animal types too, like horses and dogs. Using
allow you to check for all animals in one go:
from evennia import utils if (utils.inherits_from(obj, "typeclasses.objects.animals.Animal"): obj.msg("The bouncer stops you in the door. He says: 'No talking animals allowed.'")
from evennia import utils def _callback(obj, text): obj.msg(text) # wait 10 seconds before sending "Echo!" to obj (which we assume is defined) deferred = utils.delay(10, _callback, obj, "Echo!", persistent=False) # code here will run immediately, not waiting for the delay to fire!
This creates an asynchronous delayed call. It will fire the given
callback function after the given number of seconds. This is a very
light wrapper over a Twisted Deferred. Normally this is run
non-persistently, which means that if the server is
before the delay is over, the callback will never run (the server
forgets it). If setting
persistent to True, the delay will be stored
in the database and survive a
@reload - but for this to work it is
susceptible to the same limitations incurred when saving to an
deferred return object can usually be ignored, but calling its
.cancel() method will abort the delay prematurely.
Note that many delayed effects can be achieved without any need for an active timer. For example if you have a trait that should recover a point every 5 seconds you might just need its value when it’s needed, but checking the current time and calculating on the fly what value it should have.
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
nothing else it can be good to look here before starting to develop a
solution of your own.
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)
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[...]"
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).
Evennia supplies two utility functions for converting text to the
to_unicode(). The difference
from Python’s in-built
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 here for more info.
Making ascii tables¶
The EvTable class (
evennia/utils/evtable.py) can be used to create
correctly formatted text tables. There is also EvForm
evennia/utils/evform.py). This reads a fixed-format text template
from a file in order to create any level of sophisticated ascii layout.
Both evtable and evform have lots of options and inputs so see the
header of each module for help.
The third-party PrettyTable module is also included in Evennia.
PrettyTable is considered deprecated in favor of EvTable since
PrettyTable cannot handle ANSI colour. PrettyTable can be found in
evennia/utils/prettytable/. See its homepage above for instructions.