Add session-awareness to EvMenu through parsing the command (making it persistent-safe). Also accept an optional session keyword for the very first output. This is only really important if caller is a player and multisession_mode>2. Made as an alternative to #1162."
This commit is contained in:
parent
7a3b3ceb76
commit
44bd403cc8
1 changed files with 63 additions and 25 deletions
|
|
@ -150,7 +150,7 @@ from django.conf import settings
|
||||||
from evennia import Command, CmdSet
|
from evennia import Command, CmdSet
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.utils.evtable import EvTable
|
||||||
from evennia.utils.ansi import ANSIString, strip_ansi
|
from evennia.utils.ansi import strip_ansi
|
||||||
from evennia.utils.utils import mod_import, make_iter, pad, m_len
|
from evennia.utils.utils import mod_import, make_iter, pad, m_len
|
||||||
from evennia.commands import cmdhandler
|
from evennia.commands import cmdhandler
|
||||||
|
|
||||||
|
|
@ -234,6 +234,9 @@ class CmdEvMenuNode(Command):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
# we store Session on the menu since this can be hard to
|
||||||
|
# get in multisession environemtns if caller is a Player.
|
||||||
|
caller.ndb._menutree._session = self.session
|
||||||
menu = caller.ndb._menutree
|
menu = caller.ndb._menutree
|
||||||
if not menu:
|
if not menu:
|
||||||
if _restore(caller):
|
if _restore(caller):
|
||||||
|
|
@ -249,7 +252,7 @@ class CmdEvMenuNode(Command):
|
||||||
if not menu:
|
if not menu:
|
||||||
# can't restore from a session
|
# can't restore from a session
|
||||||
err = "Menu object not found as %s.ndb._menutree!" % (orig_caller)
|
err = "Menu object not found as %s.ndb._menutree!" % (orig_caller)
|
||||||
orig_caller.msg(err)
|
orig_caller.msg(err) # don't use session here, it's a backup
|
||||||
raise EvMenuError(err)
|
raise EvMenuError(err)
|
||||||
|
|
||||||
# we have a menu, use it.
|
# we have a menu, use it.
|
||||||
|
|
@ -394,7 +397,7 @@ def evtable_parse_input(menuobject, raw_string, caller):
|
||||||
goto, callback = menuobject.default
|
goto, callback = menuobject.default
|
||||||
menuobject.callback_goto(callback, goto, raw_string)
|
menuobject.callback_goto(callback, goto, raw_string)
|
||||||
else:
|
else:
|
||||||
caller.msg(_HELP_NO_OPTION_MATCH)
|
caller.msg(_HELP_NO_OPTION_MATCH, session=menuobject._session)
|
||||||
|
|
||||||
if not (menuobject.options or menuobject.default):
|
if not (menuobject.options or menuobject.default):
|
||||||
# no options - we are at the end of the menu.
|
# no options - we are at the end of the menu.
|
||||||
|
|
@ -420,7 +423,8 @@ class EvMenu(object):
|
||||||
options_formatter=evtable_options_formatter,
|
options_formatter=evtable_options_formatter,
|
||||||
node_formatter=underline_node_formatter,
|
node_formatter=underline_node_formatter,
|
||||||
input_parser=evtable_parse_input,
|
input_parser=evtable_parse_input,
|
||||||
persistent=False, startnode_input="", **kwargs):
|
persistent=False, startnode_input="", session=None,
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize the menu tree and start the caller onto the first node.
|
Initialize the menu tree and start the caller onto the first node.
|
||||||
|
|
||||||
|
|
@ -509,6 +513,11 @@ class EvMenu(object):
|
||||||
startnode_input (str, optional): Send an input text to `startnode` as if
|
startnode_input (str, optional): Send an input text to `startnode` as if
|
||||||
a user input text from a fictional previous node. When the server reloads,
|
a user input text from a fictional previous node. When the server reloads,
|
||||||
the latest visited node will be re-run using this kwarg.
|
the latest visited node will be re-run using this kwarg.
|
||||||
|
session (Session, optional): This is useful when calling EvMenu from a player
|
||||||
|
in multisession mode > 2. Note that this session only really relevant
|
||||||
|
for the very first display of the first node - after that, EvMenu itself
|
||||||
|
will keep the session updated from the command input. So a persistent
|
||||||
|
menu will *not* be using this same session anymore after a reload.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
any (any): All kwargs will become initialization variables on `caller.ndb._menutree`,
|
any (any): All kwargs will become initialization variables on `caller.ndb._menutree`,
|
||||||
|
|
@ -518,10 +527,15 @@ class EvMenu(object):
|
||||||
EvMenuError: If the start/end node is not found in menu tree.
|
EvMenuError: If the start/end node is not found in menu tree.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
In persistent mode, all nodes, formatters and callbacks in
|
While running, the menu is stored on the caller as `caller.ndb._menutree`. Also
|
||||||
the menu must be possible to be *pickled*, this excludes
|
the current Session (from the Command, so this is still valid in multisession
|
||||||
e.g. callables that are class methods or functions defined
|
environments) is available through `caller.ndb._menutree._session`. The `_menutree`
|
||||||
dynamically or as part of another function. In
|
property is a good one for storing intermediary data on between nodes since it
|
||||||
|
will be automatically deleted when the menu closes.
|
||||||
|
|
||||||
|
In persistent mode, all nodes, formatters and callbacks in the menu must be
|
||||||
|
possible to be *pickled*, this excludes e.g. callables that are class methods
|
||||||
|
or functions defined dynamically or as part of another function. In
|
||||||
non-persistent mode no such restrictions exist.
|
non-persistent mode no such restrictions exist.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -543,8 +557,11 @@ class EvMenu(object):
|
||||||
self.auto_quit = auto_quit
|
self.auto_quit = auto_quit
|
||||||
self.auto_look = auto_look
|
self.auto_look = auto_look
|
||||||
self.auto_help = auto_help
|
self.auto_help = auto_help
|
||||||
|
self._session = session
|
||||||
if isinstance(cmd_on_exit, str):
|
if isinstance(cmd_on_exit, str):
|
||||||
self.cmd_on_exit = lambda caller, menu: caller.execute_cmd(cmd_on_exit)
|
# At this point menu._session will have been replaced by the
|
||||||
|
# menu command to the actual session calling.
|
||||||
|
self.cmd_on_exit = lambda caller, menu: caller.execute_cmd(cmd_on_exit, session=menu._session)
|
||||||
elif callable(cmd_on_exit):
|
elif callable(cmd_on_exit):
|
||||||
self.cmd_on_exit = cmd_on_exit
|
self.cmd_on_exit = cmd_on_exit
|
||||||
else:
|
else:
|
||||||
|
|
@ -580,7 +597,7 @@ class EvMenu(object):
|
||||||
"persistent": persistent,}))
|
"persistent": persistent,}))
|
||||||
caller.attributes.add("_menutree_saved_startnode", (startnode, startnode_input))
|
caller.attributes.add("_menutree_saved_startnode", (startnode, startnode_input))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err))
|
caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err), session=self._session)
|
||||||
logger.log_trace(_TRACE_PERSISTENT_SAVING)
|
logger.log_trace(_TRACE_PERSISTENT_SAVING)
|
||||||
persistent = False
|
persistent = False
|
||||||
|
|
||||||
|
|
@ -665,7 +682,7 @@ class EvMenu(object):
|
||||||
try:
|
try:
|
||||||
node = self._menutree[nodename]
|
node = self._menutree[nodename]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.caller.msg(_ERR_NOT_IMPLEMENTED.format(nodename=nodename))
|
self.caller.msg(_ERR_NOT_IMPLEMENTED.format(nodename=nodename), session=self._session)
|
||||||
raise EvMenuError
|
raise EvMenuError
|
||||||
try:
|
try:
|
||||||
# the node should return data as (text, options)
|
# the node should return data as (text, options)
|
||||||
|
|
@ -676,20 +693,20 @@ class EvMenu(object):
|
||||||
# a normal node, only accepting caller
|
# a normal node, only accepting caller
|
||||||
nodetext, options = node(self.caller)
|
nodetext, options = node(self.caller)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.caller.msg(_ERR_NOT_IMPLEMENTED.format(nodename=nodename))
|
self.caller.msg(_ERR_NOT_IMPLEMENTED.format(nodename=nodename), session=self._session)
|
||||||
raise EvMenuError
|
raise EvMenuError
|
||||||
except Exception:
|
except Exception:
|
||||||
self.caller.msg(_ERR_GENERAL.format(nodename=nodename))
|
self.caller.msg(_ERR_GENERAL.format(nodename=nodename), session=self._session)
|
||||||
raise
|
raise
|
||||||
return nodetext, options
|
return nodetext, options
|
||||||
|
|
||||||
|
|
||||||
def display_nodetext(self):
|
def display_nodetext(self):
|
||||||
self.caller.msg(self.nodetext)
|
self.caller.msg(self.nodetext, session=self._session)
|
||||||
|
|
||||||
|
|
||||||
def display_helptext(self):
|
def display_helptext(self):
|
||||||
self.caller.msg(self.helptext)
|
self.caller.msg(self.helptext, session=self._session)
|
||||||
|
|
||||||
|
|
||||||
def callback_goto(self, callback, goto, raw_string):
|
def callback_goto(self, callback, goto, raw_string):
|
||||||
|
|
@ -720,7 +737,7 @@ class EvMenu(object):
|
||||||
# normal callable, only the caller as arg
|
# normal callable, only the caller as arg
|
||||||
nodename(self.caller)
|
nodename(self.caller)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.caller.msg(_ERR_GENERAL.format(nodename=nodename))
|
self.caller.msg(_ERR_GENERAL.format(nodename=nodename), self._session)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
# nodename is a string; lookup as node
|
# nodename is a string; lookup as node
|
||||||
|
|
@ -825,23 +842,23 @@ class CmdGetInput(Command):
|
||||||
def func(self):
|
def func(self):
|
||||||
"This is called when user enters anything."
|
"This is called when user enters anything."
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
callback = caller.ndb._getinputcallback
|
callback = caller.ndb._getinput._callback
|
||||||
if not callback:
|
if not callback:
|
||||||
# this can be happen if called from a player-command when IC
|
# this can be happen if called from a player-command when IC
|
||||||
caller = self.player
|
caller = self.player
|
||||||
callback = caller.ndb._getinputcallback
|
callback = caller.ndb._getinput._callback
|
||||||
if not callback:
|
if not callback:
|
||||||
raise RuntimeError("No input callback found.")
|
raise RuntimeError("No input callback found.")
|
||||||
|
|
||||||
prompt = caller.ndb._getinputprompt
|
caller.ndb._getinput._session = self.session
|
||||||
|
prompt = caller.ndb._getinput._prompt
|
||||||
result = self.raw_string.strip() # we strip the ending line break caused by sending
|
result = self.raw_string.strip() # we strip the ending line break caused by sending
|
||||||
|
|
||||||
ok = not callback(caller, prompt, result)
|
ok = not callback(caller, prompt, result)
|
||||||
if ok:
|
if ok:
|
||||||
# only clear the state if the callback does not return
|
# only clear the state if the callback does not return
|
||||||
# anything
|
# anything
|
||||||
del caller.ndb._getinputcallback
|
del caller.ndb._getinput
|
||||||
del caller.ndb._getinputprompt
|
|
||||||
caller.cmdset.remove(InputCmdSet)
|
caller.cmdset.remove(InputCmdSet)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -861,7 +878,12 @@ class InputCmdSet(CmdSet):
|
||||||
self.add(CmdGetInput())
|
self.add(CmdGetInput())
|
||||||
|
|
||||||
|
|
||||||
def get_input(caller, prompt, callback):
|
class _Prompt(object):
|
||||||
|
"Dummy holder"
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_input(caller, prompt, callback, session=None):
|
||||||
"""
|
"""
|
||||||
This is a helper function for easily request input from
|
This is a helper function for easily request input from
|
||||||
the caller.
|
the caller.
|
||||||
|
|
@ -880,6 +902,12 @@ def get_input(caller, prompt, callback):
|
||||||
the input prompt will be cleaned up and exited. If
|
the input prompt will be cleaned up and exited. If
|
||||||
returning True, the prompt will remain and continue to
|
returning True, the prompt will remain and continue to
|
||||||
accept input.
|
accept input.
|
||||||
|
session (Session, optional): This allows to specify the
|
||||||
|
session to send the prompt to. It's usually only
|
||||||
|
needed if `caller` is a Player in multisession modes
|
||||||
|
greater than 2. The session is then updated by the
|
||||||
|
command and is available (for example in callbacks)
|
||||||
|
through `caller.ndb.getinput._session`.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If the given callback is not callable.
|
RuntimeError: If the given callback is not callable.
|
||||||
|
|
@ -891,13 +919,23 @@ def get_input(caller, prompt, callback):
|
||||||
client inputs. So make sure to strip that before
|
client inputs. So make sure to strip that before
|
||||||
doing a comparison.
|
doing a comparison.
|
||||||
|
|
||||||
|
When the prompt is running, a temporary object
|
||||||
|
`caller.ndb._getinput` is stored; this will be removed
|
||||||
|
when the prompt finishes.
|
||||||
|
If you need the specific Session of the caller (which
|
||||||
|
may not be easy to get if caller is a player in higher
|
||||||
|
multisession modes), then it is available in the
|
||||||
|
callback through `caller.ndb._getinput._session`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not callable(callback):
|
if not callable(callback):
|
||||||
raise RuntimeError("get_input: input callback is not callable.")
|
raise RuntimeError("get_input: input callback is not callable.")
|
||||||
caller.ndb._getinputcallback = callback
|
caller.ndb._getinput = _Prompt()
|
||||||
caller.ndb._getinputprompt = prompt
|
caller.ndb._getinput._callback = callback
|
||||||
|
caller.ndb._getinput._prompt = prompt
|
||||||
|
caller.ndb._getinput._session = session
|
||||||
caller.cmdset.add(InputCmdSet)
|
caller.cmdset.add(InputCmdSet)
|
||||||
caller.msg(prompt)
|
caller.msg(prompt, session=session)
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue