mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
297 lines
No EOL
12 KiB
Markdown
297 lines
No EOL
12 KiB
Markdown
# Coding Utils
|
|
|
|
|
|
Evennia comes with many utilities to help with common coding tasks. Most are accessible directly
|
|
from the flat API, otherwise you can find them in the `evennia/utils/` folder.
|
|
|
|
## Searching
|
|
|
|
A common thing to do is to search for objects. 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 self object:
|
|
|
|
```python
|
|
obj = self.search(objname)
|
|
```
|
|
|
|
The most common time one needs to do this is inside a command body. `obj =
|
|
self.caller.search(objname)` will search inside the caller's (typically, the character that typed
|
|
the command) `.contents` (their "inventory") and `.location` (their "room").
|
|
|
|
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 functions in
|
|
`evennia.utils.search`. You can access these as shortcuts `evennia.search_*`.
|
|
|
|
```python
|
|
from evennia import search_object
|
|
obj = search_object(objname)
|
|
```
|
|
|
|
- [evennia.search_account](../wiki/evennia.accounts.manager#accountdbmanagersearch_account)
|
|
- [evennia.search_object](../wiki/evennia.objects.manager#objectdbmanagersearch_object)
|
|
- [evennia.search_object_by_tag](../wiki/evennia.utils.search#search_object_by_tag)
|
|
- [evennia.search_script](../wiki/evennia.scripts.manager#scriptdbmanagersearch_script)
|
|
- [evennia.search_channel](../wiki/evennia.comms.managers#channeldbmanagersearch_channel)
|
|
- [evennia.search_message](../wiki/evennia.comms.managers#msgmanagersearch_message)
|
|
- [evennia.search_help](../wiki/evennia.help.manager#helpentrymanagersearch_help)
|
|
|
|
Note that these latter methods will always return a `list` of results, even if the list has one or
|
|
zero entries.
|
|
|
|
## 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).
|
|
```python
|
|
import evennia
|
|
|
|
myobj = evennia.create_objects("game.gamesrc.objects.myobj.MyObj", key="MyObj")
|
|
```
|
|
|
|
- [evennia.create_account](../wiki/evennia.utils.create#create_account)
|
|
- [evennia.create_object](../wiki/evennia.utils.create#create_object)
|
|
- [evennia.create_script](../wiki/evennia.utils.create#create_script)
|
|
- [evennia.create_channel](../wiki/evennia.utils.create#create_channel)
|
|
- [evennia.create_help_entry](../wiki/evennia.utils.create#create_help_entry)
|
|
- [evennia.create_message](../wiki/evennia.utils.create#create_message)
|
|
|
|
Each of these create-functions have a host of arguments to further customize the created entity. See
|
|
`evennia/utils/create.py` for more information.
|
|
|
|
## Logging
|
|
|
|
Normally you can use Python `print` statements to see output to the terminal/log. The `print`
|
|
statement should only be used for debugging though. For producion output, use the `logger` which
|
|
will create proper logs either to terminal or to file.
|
|
|
|
```python
|
|
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.
|
|
|
|
```python
|
|
try:
|
|
# [some code that may fail...]
|
|
except Exception:
|
|
logger.log_trace("This text will show beneath the traceback itself.")
|
|
```
|
|
|
|
The `log_file` logger, finally, is a very useful logger for outputting arbitrary log messages. This
|
|
is a heavily optimized asynchronous log mechanism using
|
|
[threads](https://en.wikipedia.org/wiki/Thread_%28computing%29) to avoid overhead. You should be
|
|
able to use it for very heavy custom logging without fearing disk-write delays.
|
|
|
|
```python
|
|
logger.log_file(message, filename="mylog.log")
|
|
```
|
|
|
|
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 `game/logs/game.log`.
|
|
|
|
## Time Utilities
|
|
### Game time
|
|
|
|
Evennia tracks the current server time. You can access this time via the `evennia.gametime`
|
|
shortcut:
|
|
|
|
```python
|
|
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()
|
|
|
|
```
|
|
|
|
The setting `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
|
|
`gametime` 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](github:evennia.utils.gametime#schedule) function:
|
|
|
|
```python
|
|
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)
|
|
```
|
|
|
|
### utils.time_format()
|
|
|
|
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* 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)
|
|
|
|
### utils.delay()
|
|
|
|
```python
|
|
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](https://twistedmatrix.com/documents/current/core/howto/defer.html). Normally this is run
|
|
non-persistently, which means that if the server is `@reload`ed 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 [Attribute](Attributes).
|
|
|
|
The `deferred` return object can usually be ignored, but calling its `.cancel()` method will abort
|
|
the delay prematurely.
|
|
|
|
`utils.delay` is the lightest form of delayed call in Evennia. For other way to create time-bound
|
|
tasks, see the [TickerHandler](TickerHandler) and [Scripts](Scripts).
|
|
|
|
> 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.
|
|
|
|
## Object Classes
|
|
### utils.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) 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
|
|
`inherits_from` will allow you to check for all animals in one go:
|
|
|
|
```python
|
|
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.'")
|
|
```
|
|
|
|
|
|
|
|
## 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 `evennia/utils/utils.py` (shortcut `evennia.utils`).
|
|
If nothing else it can be good to look here before starting to develop a solution of your own.
|
|
|
|
### utils.fill()
|
|
|
|
This flood-fills a text to a given width (shuffles the words to make each line evenly wide). It also
|
|
indents as needed.
|
|
|
|
```python
|
|
outtxt = fill(intxt, width=78, indent=4)
|
|
```
|
|
|
|
### utils.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.
|
|
|
|
```python
|
|
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[...]"
|
|
```
|
|
|
|
### utils.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
|
|
#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).
|
|
|
|
### to_str() and to_bytes()
|
|
|
|
Evennia supplies two utility functions for converting text to the correct
|
|
encodings. `to_str()` and `to_bytes()`. Unless you are adding a custom protocol and
|
|
need to send byte-data over the wire, `to_str` is the only one you'll need.
|
|
|
|
The difference from Python's in-built `str()` and `bytes()` 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](../Concept/Text-Encodings) for more info.
|
|
|
|
### Ansi Coloring Tools
|
|
- [evennia.ansi](api:evennia.utils.ansi)
|
|
|
|
## Display utilities
|
|
### Making ascii tables
|
|
|
|
The [EvTable](github:evennia.utils.evtable#evtable) class (`evennia/utils/evtable.py`) can be used
|
|
to create correctly formatted text tables. There is also
|
|
[EvForm](github:evennia.utils.evform#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](https://code.google.com/p/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.
|
|
|
|
### Menus
|
|
- [evennia.EvMenu](github:evennia.utils.evmenu#evmenu) |