mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Add @interactive decorator to use yield to pause in a method
This commit is contained in:
parent
866e8611b7
commit
a2bc979503
2 changed files with 81 additions and 13 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -15,10 +15,10 @@ Update to Python 3
|
|||
- Add new `@force` command to have another object perform a command.
|
||||
- Add the Portal uptime to the `@time` command.
|
||||
- Make the `@link` command first make a local search before a global search.
|
||||
- Have the default Unloggedin-look command look for optional `connection_screen()` callable in
|
||||
`mygame/server/conf/connection_screen.py`. This allows for more flexible welcome screens
|
||||
that are calculated on the fly.
|
||||
- `@py` command now defaults to escaping html tags in its output when viewing in the webclient.
|
||||
- Have the default Unloggedin-look command look for optional `connection_screen()` callable in
|
||||
`mygame/server/conf/connection_screen.py`. This allows for more flexible welcome screens
|
||||
that are calculated on the fly.
|
||||
- `@py` command now defaults to escaping html tags in its output when viewing in the webclient.
|
||||
Use new `/clientraw` switch to get old behavior (issue #1369).
|
||||
|
||||
### Web
|
||||
|
|
@ -73,7 +73,7 @@ Web/Django standard initiative (@strikaco)
|
|||
|
||||
### Prototypes
|
||||
|
||||
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
|
||||
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
|
||||
argument (`prototype`) instead of having to give it as `**prototype`.
|
||||
- `evennia.prototypes.search_prototype` has a new kwarg `require_single=False` that
|
||||
raises a KeyError exception if query gave 0 or >1 results.
|
||||
|
|
@ -106,7 +106,7 @@ Web/Django standard initiative (@strikaco)
|
|||
### Server
|
||||
|
||||
- Convert ServerConf model to store its values as a Picklefield (same as Attributes) instead of using a custom solution.
|
||||
- OOB: Add support for MSDP LIST, REPORT, UNREPORT commands (re-mapped to `msdp_list`,
|
||||
- OOB: Add support for MSDP LIST, REPORT, UNREPORT commands (re-mapped to `msdp_list`,
|
||||
`msdp_report`, `msdp_unreport` inlinefuncs_)
|
||||
- Added `evennia.ANSIString` to flat API.
|
||||
- Server/Portal log files now cycle to names on the form `server_.log_19_03_08_` instead of `server.log___19.3.8`, retaining
|
||||
|
|
@ -116,6 +116,9 @@ Web/Django standard initiative (@strikaco)
|
|||
|
||||
- `evennia` launcher now fully handles all django-admin commands, like running tests in parallel.
|
||||
- `evennia.utils.create.account` now also takes `tags` and `attrs` keywords.
|
||||
- `evennia.utils.interactive` decorator can now allow you to use yield(secs) to pause operation
|
||||
in any function, not just in Command.func. Likewise, response = yield(question) will work
|
||||
if the decorated function has an argument or kwarg `caller`.
|
||||
- Added many more unit tests.
|
||||
- Swap argument order of `evennia.set_trace` to `set_trace(term_size=(140, 40), debugger='auto')`
|
||||
since the size is more likely to be changed on the command line.
|
||||
|
|
@ -134,7 +137,7 @@ Web/Django standard initiative (@strikaco)
|
|||
|
||||
### Contribs
|
||||
|
||||
- The `extended_room` contrib saw some backwards-incompatible refactoring:
|
||||
- The `extended_room` contrib saw some backwards-incompatible refactoring:
|
||||
+ All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
|
||||
it's `CmdExtendedRoomLook` etc.
|
||||
+ The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
|
||||
|
|
@ -142,7 +145,7 @@ Web/Django standard initiative (@strikaco)
|
|||
command works in the tutorial-world.
|
||||
+ The `detail` command now also supports deleting details (like the tutorial-world version).
|
||||
+ The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
|
||||
to install the extended-room contrib.
|
||||
to install the extended-room contrib.
|
||||
- Reworked `menu_login` contrib to use latest EvMenu standards. Now also supports guest logins.
|
||||
- Mail contrib was refactored to have optional Command classes `CmdMail` for OOC+IC mail (added
|
||||
to the CharacterCmdSet and `CmdMailCharacter` for IC-only mailing between chars (added to CharacterCmdSet)
|
||||
|
|
@ -260,7 +263,7 @@ Web/Django standard initiative (@strikaco)
|
|||
- `tb_items` - Extends `tb_equip` with item use with conditions/status effects.
|
||||
- `tb_magic` - Extends `tb_equip` with spellcasting.
|
||||
- `tb_range` - Adds system for abstract positioning and movement.
|
||||
- The `extended_room` contrib saw some backwards-incompatible refactoring:
|
||||
- The `extended_room` contrib saw some backwards-incompatible refactoring:
|
||||
- All commands now begin with `CmdExtendedRoom`. So before it was `CmdExtendedLook`, now
|
||||
it's `CmdExtendedRoomLook` etc.
|
||||
- The `detail` command was broken out of the `desc` command and is now a new, stand-alone command
|
||||
|
|
@ -268,11 +271,11 @@ Web/Django standard initiative (@strikaco)
|
|||
command works in the tutorial-world.
|
||||
- The `detail` command now also supports deleting details (like the tutorial-world version).
|
||||
- The new `ExtendedRoomCmdSet` includes all the extended-room commands and is now the recommended way
|
||||
to install the extended-room contrib.
|
||||
to install the extended-room contrib.
|
||||
- Updates and some cleanup of existing contribs.
|
||||
|
||||
|
||||
### Internationalization
|
||||
### Internationalization
|
||||
|
||||
- Polish translation by user ogotai
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ import math
|
|||
import re
|
||||
import textwrap
|
||||
import random
|
||||
import pickle
|
||||
import inspect
|
||||
from twisted.internet.task import deferLater
|
||||
from os.path import join as osjoin
|
||||
from importlib import import_module
|
||||
from importlib.util import find_spec, module_from_spec
|
||||
from importlib.util import find_spec
|
||||
from inspect import ismodule, trace, getmembers, getmodule, getmro
|
||||
from collections import defaultdict, OrderedDict
|
||||
from twisted.internet import threads, reactor, task
|
||||
|
|
@ -1981,3 +1982,67 @@ def get_all_typeclasses(parent=None):
|
|||
typeclasses = {name: typeclass for name, typeclass in typeclasses.items()
|
||||
if inherits_from(typeclass, parent)}
|
||||
return typeclasses
|
||||
|
||||
|
||||
def interactive(func):
|
||||
"""
|
||||
Decorator to make a method pausable with yield(seconds)
|
||||
and able to ask for user-input with response=yield(question).
|
||||
For the question-asking to work, 'caller' must the name
|
||||
of an argument or kwarg to the decorated function.
|
||||
|
||||
Note that this turns the method into a generator.
|
||||
|
||||
Example usage:
|
||||
|
||||
@interactive
|
||||
def myfunc(caller):
|
||||
caller.msg("This is a test")
|
||||
# wait five seconds
|
||||
yield(5)
|
||||
# ask user (caller) a question
|
||||
response = yield("Do you want to continue waiting?")
|
||||
if response == "yes":
|
||||
yield(5)
|
||||
else:
|
||||
# ...
|
||||
|
||||
"""
|
||||
from evennia.utils.evmenu import get_input
|
||||
def _process_input(caller, prompt, result, generator):
|
||||
deferLater(reactor, 0, _iterate, generator, caller, response=result)
|
||||
return False
|
||||
|
||||
def _iterate(generator, caller=None, response=None):
|
||||
try:
|
||||
if response is None:
|
||||
value = next(generator)
|
||||
else:
|
||||
value = generator.send(response)
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
if isinstance(value, (int, float)):
|
||||
delay(value, _iterate, generator, caller=caller)
|
||||
elif isinstance(value, str):
|
||||
if not caller:
|
||||
raise ValueError("To retrieve input from a @pausable method, that method "
|
||||
"must be called with a 'caller' argument)")
|
||||
get_input(caller, value, _process_input, generator=generator)
|
||||
else:
|
||||
raise ValueError("yield(val) in a @pausable method must have an int/float as arg.")
|
||||
|
||||
def decorator(*args, **kwargs):
|
||||
argnames = inspect.getfullargspec(func).args
|
||||
caller = None
|
||||
if 'caller' in argnames:
|
||||
# we assume this is an object
|
||||
caller = args[argnames.index('caller')]
|
||||
|
||||
ret = func(*args, **kwargs)
|
||||
if isinstance(ret, types.GeneratorType):
|
||||
_iterate(ret, caller)
|
||||
else:
|
||||
return ret
|
||||
|
||||
return decorator
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue