mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Elaborate more on protfunc usage. Resolve #3108
This commit is contained in:
parent
1d94d77248
commit
0e71e00fae
3 changed files with 73 additions and 45 deletions
|
|
@ -1,6 +1,28 @@
|
|||
# FuncParser inline text parsing
|
||||
|
||||
The [FuncParser](evennia.utils.funcparser.FuncParser) extracts and executes 'inline functions' embedded in a string on the form `$funcname(args, kwargs)`. Under the hood, this will lead to a call to a Python function you control. The inline function call will be replaced by the return from the function.
|
||||
The [FuncParser](evennia.utils.funcparser.FuncParser) extracts and executes 'inline functions' embedded in a string on the form `$funcname(args, kwargs)`, executes the matching 'inline function' and replaces the call with the return from the call.
|
||||
|
||||
To test it, let's tell Evennia to apply the Funcparser on every outgoing message. This is disabled by default (not everyone needs this functionality). To activate, add to your settings file:
|
||||
|
||||
FUNCPARSER_PARSE_OUTGOING_MESSAGES_ENABLED = True
|
||||
|
||||
After a reload, you can try this in-game
|
||||
|
||||
```{shell}
|
||||
> say I got $randint(1,5) gold!
|
||||
You say "I got 3 gold!"
|
||||
```
|
||||
|
||||
To escape the inlinefunc (e.g. to explain to someone how it works, use `$$`)
|
||||
|
||||
```{shell}
|
||||
> say To get a random value from 1 to 5, use $$randint(1,5).
|
||||
You say "To get a random value from 1 to 5, use $randint(1,5)."
|
||||
```
|
||||
|
||||
While `randint` may look and work just like `random.randint` from the standard Python library, it is _not_. Instead it's a `inlinefunc` named `randint` made available to Evennia (which in turn uses the standard library function). For security reasons, only functions explicitly assigned to be used as inlinefuncs are viable.
|
||||
|
||||
The `FuncParser` tool is initialized with the inlinefuncs it's supposed to recognize. Below is an example of a parser only only understanding a single `$pow` inlinefunc:
|
||||
|
||||
```python
|
||||
from evennia.utils.funcparser import FuncParser
|
||||
|
|
@ -28,14 +50,6 @@ parser.parse_to_any("$pow(4)")
|
|||
16
|
||||
```
|
||||
|
||||
To show a `$func()` verbatim in your code without parsing it, escape it as either `$$func()` or `\$func()`:
|
||||
|
||||
|
||||
```python
|
||||
parser.parse("This is an escaped $$pow(4) and so is this \$pow(3)")
|
||||
"This is an escaped $pow(4) and so is this $pow(3)"
|
||||
```
|
||||
|
||||
## Working with FuncParser
|
||||
|
||||
The FuncParser can be applied to any string. Out of the box it's applied in a few situations:
|
||||
|
|
@ -261,9 +275,7 @@ It may be tempting to run use Python's in-built ``eval()`` or ``exec()`` functio
|
|||
|
||||
## Default funcparser callables
|
||||
|
||||
These are some example callables you can import and add your parser. They are divided into
|
||||
global-level dicts in `evennia.utils.funcparser`. Just import the dict(s) and merge/add one or
|
||||
more to them when you create your `FuncParser` instance to have those callables be available.
|
||||
These are some example callables you can import and add your parser. They are divided into global-level dicts in `evennia.utils.funcparser`. Just import the dict(s) and merge/add one or more to them when you create your `FuncParser` instance to have those callables be available.
|
||||
|
||||
### `evennia.utils.funcparser.FUNCPARSER_CALLABLES`
|
||||
|
||||
|
|
@ -331,6 +343,16 @@ Here the `caller` is the one sending the message and `receiver` the one to see i
|
|||
- `$pron(pronoun [,options])` ([code](evennia.utils.funcparser.funcparser_callable_pronoun)) - Dynamically
|
||||
map pronouns (like his, herself, you, its etc) between 1st/2nd person to 3rd person.
|
||||
|
||||
|
||||
### `evennia.prototypes.protfuncs`
|
||||
|
||||
This is used by the [Prototype system](./Prototypes.md) and allows for adding references inside the prototype. The funcparsing will happen before the spawn.
|
||||
|
||||
Available inlinefuncs to prototypes:
|
||||
|
||||
- All `FUNCPARSER_CALLABLES` and `SEARCHING_CALLABLES`
|
||||
- `$protkey(key)` - returns the value of another key within the same prototype. Note that the system will try to convert this to a 'real' value (like turning the string "3" into the integer 3), for security reasons, not all embedded values can be converted this way. Note however that you can do nested calls with inlinefuncs, including adding your own converters.
|
||||
|
||||
### Example
|
||||
|
||||
Here's an example of including the default callables together with two custom ones.
|
||||
|
|
|
|||
|
|
@ -20,15 +20,11 @@ The *spawner* takes a prototype and uses it to create (spawn) new, custom object
|
|||
|
||||
### Using the OLC
|
||||
|
||||
Enter the `olc` command or `spawn/olc` to enter the prototype wizard. This is a menu system for
|
||||
creating, loading, saving and manipulating prototypes. It's intended to be used by in-game builders
|
||||
and will give a better understanding of prototypes in general. Use `help` on each node of the menu
|
||||
for more information. Below are further details about how prototypes work and how they are used.
|
||||
Enter the `olc` command or `spawn/olc` to enter the prototype wizard. This is a menu system for creating, loading, saving and manipulating prototypes. It's intended to be used by in-game builders and will give a better understanding of prototypes in general. Use `help` on each node of the menu for more information. Below are further details about how prototypes work and how they are used.
|
||||
|
||||
### The prototype
|
||||
|
||||
The prototype dictionary can either be created for you by the OLC (see above), be written manually in a Python module (and then referenced by the `spawn` command/OLC), or created on-the-fly and
|
||||
manually loaded into the spawner function or `spawn` command.
|
||||
The prototype dictionary can either be created for you by the OLC (see above), be written manually in a Python module (and then referenced by the `spawn` command/OLC), or created on-the-fly and manually loaded into the spawner function or `spawn` command.
|
||||
|
||||
The dictionary defines all possible database-properties of an Object. It has a fixed set of allowed keys. When preparing to store the prototype in the database (or when using the OLC), some of these keys are mandatory. When just passing a one-time prototype-dict to the spawner the system is more lenient and will use defaults for keys not explicitly provided.
|
||||
|
||||
|
|
@ -54,15 +50,12 @@ All keys starting with `prototype_` are for book keeping.
|
|||
- `prototype_key` - the 'name' of the prototype, used for referencing the prototype
|
||||
when spawning and inheritance. If defining a prototype in a module and this
|
||||
not set, it will be auto-set to the name of the prototype's variable in the module.
|
||||
- `prototype_parent` - If given, this should be the `prototype_key` of another prototype stored in
|
||||
the system or available in a module. This makes this prototype *inherit* the keys from the
|
||||
parent and only override what is needed. Give a tuple `(parent1, parent2, ...)` for multiple
|
||||
left-right inheritance. If this is not given, a `typeclass` should usually be defined (below).
|
||||
- `prototype_parent` - If given, this should be the `prototype_key` of another prototype stored in the system or available in a module. This makes this prototype *inherit* the keys from the
|
||||
parent and only override what is needed. Give a tuple `(parent1, parent2, ...)` for multiple left-right inheritance. If this is not given, a `typeclass` should usually be defined (below).
|
||||
- `prototype_desc` - this is optional and used when listing the prototype in in-game listings.
|
||||
- `protototype_tags` - this is optional and allows for tagging the prototype in order to find it
|
||||
easier later.
|
||||
- `prototype_locks` - two lock types are supported: `edit` and `spawn`. The first lock restricts the copying and editing of the prototype when loaded through the OLC. The second determines who
|
||||
may use the prototype to create new objects.
|
||||
- `prototype_locks` - two lock types are supported: `edit` and `spawn`. The first lock restricts the copying and editing of the prototype when loaded through the OLC. The second determines who may use the prototype to create new objects.
|
||||
|
||||
|
||||
The remaining keys determine actual aspects of the objects to spawn from this prototype:
|
||||
|
|
@ -121,7 +114,10 @@ Finally, the value can be a *prototype function* (*Protfunc*). These look like s
|
|||
"attrs": {"desc": "This is a large $red(and very red) demon. "
|
||||
"He has $randint(2,5) skulls in a chain around his neck."}
|
||||
```
|
||||
At execution time, the place of the protfunc will be replaced with the result of that protfunc being called (this is always a string). A protfunc is a [FuncParser function](./FuncParser.md) run every time the prototype is used to spawn a new object.
|
||||
|
||||
> If you want to escape a protfunc and have it appear verbatim, use `$$funcname()`.
|
||||
|
||||
At spawn time, the place of the protfunc will be replaced with the result of that protfunc being called (this is always a string). A protfunc is a [FuncParser function](./FuncParser.md) run every time the prototype is used to spawn a new object. See the FuncParse for a lot more information.
|
||||
|
||||
Here is how a protfunc is defined (same as other funcparser functions).
|
||||
|
||||
|
|
@ -137,7 +133,12 @@ def red(*args, **kwargs):
|
|||
return f"|r{args[0]}|n"
|
||||
```
|
||||
|
||||
> Note that we must make sure to validate input and raise `ValueError` if that fails. Also, it is *not* possible to use keywords in the call to the protfunc (so something like `$echo(text, align=left)` is invalid). The `kwargs` requred is for internal evennia use and not used at all for protfuncs (only by inlinefuncs).
|
||||
> Note that we must make sure to validate input and raise `ValueError` on failure.
|
||||
|
||||
The parser will always include the following reserved `kwargs`:
|
||||
- `session` - the current [Session](evennia.server.ServerSession) performing the spawning.
|
||||
- `prototype` - The Prototype-dict this function is a part of. This is intended to be used _read-only_. Be careful to modify a mutable structure like this from inside the function - you can cause really hard-to-find bugs this way.
|
||||
- `current_key` - The current key of the `prototype` dict under which this protfunc is executed.
|
||||
|
||||
To make this protfunc available to builders in-game, add it to a new module and add the path to that module to `settings.PROT_FUNC_MODULES`:
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ The default protfuncs available out of the box are defined in `evennia/prototype
|
|||
|
||||
| Protfunc | Description |
|
||||
| --- | --- |
|
||||
| `$random()` | Returns random value in range [0, 1) |
|
||||
| `$random()` | Returns random value in range `[0, 1)` |
|
||||
| `$randint(start, end)` | Returns random value in range [start, end] |
|
||||
| `$left_justify(<text>)` | Left-justify text |
|
||||
| `$right_justify(<text>)` | Right-justify text to screen width |
|
||||
|
|
@ -170,7 +171,7 @@ The default protfuncs available out of the box are defined in `evennia/prototype
|
|||
| `$objlist(<query>)` | Like `$obj`, except always returns a list of zero, one or more results. |
|
||||
| `$dbref(dbref)` | Returns argument if it is formed as a #dbref (e.g. #1234), otherwise error. |
|
||||
|
||||
For developers with access to Python, using protfuncs in prototypes is generally not useful. Passing real Python functions is a lot more powerful and flexible. Their main use is to allow in-game builders to do limited coding/scripting for their prototypes without giving them direct access to raw Python.
|
||||
For developers with access to Python, using protfuncs in prototypes is generally not useful. Passing real Python functions is a lot more powerful and flexible. Their main use is to allow in-game builders to do limited coding/scripting for their prototypes without giving them direct access to raw Python.
|
||||
|
||||
## Database prototypes
|
||||
|
||||
|
|
@ -203,10 +204,7 @@ Here is an example of a prototype defined in a module:
|
|||
"health": 20}
|
||||
```
|
||||
|
||||
> Note that in the example above, `"ORC_SHAMAN"` will become the `prototype_key` of this prototype.
|
||||
> It's the only case when `prototype_key` can be skipped in a prototype. However, if `prototype_key`
|
||||
> was given explicitly, that would take precedence. This is a legacy behavior and it's recommended
|
||||
> that you always add `prototype_key` to be consistent.
|
||||
> Note that in the example above, `"ORC_SHAMAN"` will become the `prototype_key` of this prototype. It's the only case when `prototype_key` can be skipped in a prototype. However, if `prototype_key`was given explicitly, that would take precedence. This is a legacy behavior and it's recommended > that you always add `prototype_key` to be consistent.
|
||||
|
||||
|
||||
## Spawning
|
||||
|
|
|
|||
|
|
@ -1,28 +1,35 @@
|
|||
"""
|
||||
Protfuncs are FuncParser-callables that can be embedded in a prototype to
|
||||
provide custom logic without having access to Python. The protfunc is parsed at
|
||||
the time of spawning, using the creating object's session as input. If the
|
||||
protfunc returns a non-string, this is what will be added to the prototype.
|
||||
Protfuncs are FuncParser-callables that can be embedded in a prototype to provide custom logic
|
||||
without having access to Python. The protfunc is parsed at the time of spawning, using the creating
|
||||
object's session as input. If the protfunc returns a non-string, this is what will be added to the
|
||||
prototype.
|
||||
|
||||
In the prototype dict, the protfunc is specified as a string inside the prototype, e.g.:
|
||||
|
||||
{ ...
|
||||
```python
|
||||
{ ...
|
||||
|
||||
"key": "$funcname(args, kwargs)"
|
||||
"key": "$funcname(args, kwargs)"
|
||||
|
||||
... }
|
||||
... }
|
||||
```
|
||||
|
||||
Available protfuncs are either all callables in one of the modules of `settings.PROT_FUNC_MODULES`
|
||||
or all callables added to a dict FUNCPARSER_CALLABLES in such a module.
|
||||
or all callables added to a dict FUNCPARSER_CALLABLES in such a module. By default, base inlinefuncs
|
||||
for text manipulation and searching are included, as well as the special `$protkey` function.
|
||||
See the Prototypes and Spawner documentation for more info.
|
||||
|
||||
def funcname (*args, **kwargs)
|
||||
```python
|
||||
def funcname (*args, **kwargs):
|
||||
return "replacement text"
|
||||
```
|
||||
|
||||
At spawn-time the spawner passes the following extra kwargs into each callable (in addition to
|
||||
what is added in the call itself):
|
||||
|
||||
- session (Session): The Session of the entity spawning using this prototype.
|
||||
- prototype (dict): The dict this protfunc is a part of.
|
||||
- current_key (str): The active key this value belongs to in the prototype.
|
||||
- `session` (Session): The Session of the entity spawning using this prototype.
|
||||
- `prototype` (dict): The dict this protfunc is a part of.
|
||||
- `current_key` (str): The active key this value belongs to in the prototype.
|
||||
|
||||
Any traceback raised by this function will be handled at the time of spawning and abort the spawn
|
||||
before any object is created/updated. It must otherwise return the value to store for the specified
|
||||
|
|
@ -36,8 +43,9 @@ from evennia.utils import funcparser
|
|||
def protfunc_callable_protkey(*args, **kwargs):
|
||||
"""
|
||||
Usage: $protkey(keyname)
|
||||
|
||||
Returns the value of another key in this prototoype. Will raise an error if
|
||||
the key is not found in this prototype.
|
||||
the key is not found in this prototype.
|
||||
|
||||
"""
|
||||
if not args:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue