mirror of
https://github.com/evennia/evennia.git
synced 2026-03-17 13:26:30 +01:00
236 lines
No EOL
12 KiB
Markdown
236 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](https://github.com/evennia/evennia/wiki/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](Text-Encodings) for more info.
|
|
|
|
### Ansi Coloring Tools
|
|
- [evennia.ansi](../wiki/evennia.utils.ansi)
|
|
|
|
## Display utilities
|
|
### Making ascii tables
|
|
|
|
The [EvTable](../wiki/evennia.utils.evtable#evtable) class (`evennia/utils/evtable.py`) can be used to create correctly formatted text tables. There is also [EvForm](../wiki/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](../wiki/evennia.utils.evmenu#evmenu) |