diff --git a/evennia/__init__.py b/evennia/__init__.py index 4ed13e7dd4..369c3e4aa5 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -60,7 +60,7 @@ create_help_entry = None # utilities settings = None lockfuncs = None -oobhandler = None +inputhandler = None logger = None gametime = None ansi = None @@ -167,7 +167,7 @@ def _init(): # handlers from .scripts.tickerhandler import TICKER_HANDLER - from .server.oobhandler import OOB_HANDLER + from .server.inputhandler import INPUT_HANDLER from .server.sessionhandler import SESSION_HANDLER from .comms.channelhandler import CHANNEL_HANDLER diff --git a/evennia/game_template/server/conf/inputhandler_funcs.py b/evennia/game_template/server/conf/inputhandler_funcs.py new file mode 100644 index 0000000000..b2aa3d848e --- /dev/null +++ b/evennia/game_template/server/conf/inputhandler_funcs.py @@ -0,0 +1,55 @@ +""" +Inputhandler functions + +Inputcommands are always called from the client (they handle server +input, hence the name). + +This module is loaded by being included in +`settings.INPUT_HANDLER_MODULES`. + +All *global functions* included in this module are +considered input-handler functions and can be called +by the client to handle input. + +An inputhandler function must have the following call signature: + + cmdname(session, *args, **kwargs) + +Where session will be the active session and *args, **kwargs are extra +incoming arguments and keyword properties. + +A special command is the "default" command, which is called when +no other cmdname matches: + + default(session, cmdname, *args, **kwargs) + +""" + +# import the contents of the default inputhandler_func module +#from evennia.server.inputhandler_funcs import * + + +# def oob_echo(session, *args, **kwargs): +# """ +# Example echo function. Echoes args, kwargs sent to it. +# +# Args: +# session (Session): The Session to receive the echo. +# args (list of str): Echo text. +# kwargs (dict of str, optional): Keyed echo text +# +# """ +# session.msg(oob=("echo", args, kwargs)) +# +# +# def default(session, cmdname, *args, **kwargs): +# """ +# Handles commands without a matching inputhandler func. +# +# Args: +# session (Session): The active Session. +# cmdname (str): The (unmatched) command name +# args, kwargs (any): Arguments to function. +# +# """ +# pass diff --git a/evennia/game_template/server/conf/oobfuncs.py b/evennia/game_template/server/conf/oobfuncs.py deleted file mode 100644 index 18e1b35652..0000000000 --- a/evennia/game_template/server/conf/oobfuncs.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -OOB configuration. - -This module should be included in (or replace) the -default module set in settings.OOB_PLUGIN_MODULES - -A function oob_error will be used as optional error management. -The available OOB commands can be extended by changing - - `settings.OOB_PLUGIN_MODULES` - -CMD_MAP: This module must contain a global dictionary CMD_MAP. This is -a dictionary that maps the call-name available to a function in this -module (this allows you to map multiple oob cmdnames to a single -actual Python function, for example). - -oob functions have the following call signature: - - function(session, *args, **kwargs) - -where session is the active session and *args, **kwargs are extra -arguments sent with the oob command. - -A function mapped to the key "oob_error" will retrieve error strings -if it is defined. It will get the error message as its 1st argument. - - oob_error(session, error, *args, **kwargs) - -This allows for customizing error handling. - -Data is usually returned to the user via a return OOB call: - - session.msg(oob=(oobcmdname, (args,), {kwargs})) - -Oobcmdnames are case-sensitive. Note that args, kwargs must be -iterable. Non-iterables will be interpreted as a new command name (you -can send multiple oob commands with one msg() call)) - -""" - -# import the contents of the default msdp module -from evennia.server.oob_cmds import * - - -# def oob_echo(session, *args, **kwargs): -# """ -# Example echo function. Echoes args, kwargs sent to it. -# -# Args: -# session (Session): The Session to receive the echo. -# args (list of str): Echo text. -# kwargs (dict of str, optional): Keyed echo text -# -# """ -# session.msg(oob=("echo", args, kwargs)) -# -## oob command map -# CMD_MAP = {"ECHO": oob_echo} diff --git a/evennia/server/inputhandler.py b/evennia/server/inputhandler.py index 4dda804e1f..7abb8e622f 100644 --- a/evennia/server/inputhandler.py +++ b/evennia/server/inputhandler.py @@ -14,17 +14,16 @@ an object's properties or start a repeating action. from builtins import object from collections import defaultdict -from django.conf import settings from evennia.server.models import ServerConfig -from evennia.server.sessionhandler import SESSIONS from evennia.scripts.tickerhandler import TickerHandler from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj from evennia.utils import logger -from evennia.utils.utils import make_iter, mod_import _SA = object.__setattr__ _GA = object.__getattribute__ _DA = object.__delattr__ +_SESSIONS = None + # set at the bottom of this module _OOB_FUNCS = None @@ -66,14 +65,18 @@ class OOBFieldMonitor(object): fieldname (str): The field to monitor """ + global _SESSIONS + if not _SESSIONS: + from evennia.server.sessionhandler import SESSIONS as _SESSIONS + for sessid, oobtuples in self.subscribers.items(): # oobtuples is a list [(oobfuncname, args, kwargs), ...], # a potential list of oob commands to call when this # field changes. - sessid = SESSIONS.get(sessid) + sessid = _SESSIONS.get(sessid) if sessid: for (oobfuncname, args, kwargs) in oobtuples: - OOB_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, self.obj, *args, **kwargs) + INPUT_HANDLER.execute_cmd(sessid, oobfuncname, fieldname, self.obj, *args, **kwargs) def add(self, session, oobfuncname, *args, **kwargs): """ @@ -123,7 +126,7 @@ class OOBAtRepeater(object): def __call__(self, *args, **kwargs): "Called at regular intervals. Calls the oob function" - OOB_HANDLER.execute_cmd(kwargs["_sessid"], kwargs["_oobfuncname"], *args, **kwargs) + INPUT_HANDLER.execute_cmd(kwargs["_sessid"], kwargs["_oobfuncname"], *args, **kwargs) # Main OOB Handler @@ -456,30 +459,30 @@ class OOBHandler(TickerHandler): # access object -OOB_HANDLER = OOBHandler() - -# load resources from plugin module. This must happen -# AFTER the OOB_HANDLER has been initialized since the -# commands will want to import it. -_OOB_FUNCS = {} -for modname in make_iter(settings.OOB_PLUGIN_MODULES): - _OOB_FUNCS.update(mod_import(modname).CMD_MAP) - -# get the command to receive eventual error strings -_OOB_ERROR = _OOB_FUNCS.get("oob_error", None) -if not _OOB_ERROR: - # no custom error set; create default oob error message function - def oob_error(session, errmsg, *args, **kwargs): - """ - Fallback error handler. This will be used if no custom - oob_error is defined and just echoes the error back to the - session. - - Args: - errmsg (str): Error message to echo. - args, kwargs (any): Not used. - - """ - session.msg(oob=("err", ("ERROR ", errmsg))) - _OOB_ERROR = oob_error +INPUT_HANDLER = OOBHandler() +## load resources from plugin module. This must happen +## AFTER the OOB_HANDLER has been initialized since the +## commands will want to import it. +#_OOB_FUNCS = {} +#for modname in make_iter(settings.OOB_PLUGIN_MODULES): +# _OOB_FUNCS.update(mod_import(modname).CMD_MAP) +# +## get the command to receive eventual error strings +#_OOB_ERROR = _OOB_FUNCS.get("oob_error", None) +#if not _OOB_ERROR: +# # no custom error set; create default oob error message function +# def oob_error(session, errmsg, *args, **kwargs): +# """ +# Fallback error handler. This will be used if no custom +# oob_error is defined and just echoes the error back to the +# session. +# +# Args: +# errmsg (str): Error message to echo. +# args, kwargs (any): Not used. +# +# """ +# session.msg(oob=("err", ("ERROR ", errmsg))) +# _OOB_ERROR = oob_error +# diff --git a/evennia/server/inputhandler_funcs.py b/evennia/server/inputhandler_funcs.py index ea2c08e608..bc0688f23d 100644 --- a/evennia/server/inputhandler_funcs.py +++ b/evennia/server/inputhandler_funcs.py @@ -58,9 +58,10 @@ name. from future.utils import viewkeys from django.conf import settings -from evennia.utils.utils import to_str +from evennia.server.inputhandler import INPUT_HANDLER from evennia.commands.cmdhandler import cmdhandler -#from evennia.server.inputhandler import INPUT_HANDLER +from evennia.utils.logger import log_err +from evennia.utils.utils import to_str _IDLE_COMMAND = settings.IDLE_COMMAND _GA = object.__getattribute__ @@ -104,6 +105,16 @@ def text(session, *args, **kwargs): cmdhandler(session, text, callertype="session", session=session) session.update_session_counters() +def default(session, cmdname, *args, **kwargs): + """ + Default catch-function. This is like all other input functions except + it will get `cmdname` as the first argument. + + """ + err = "Input command not recognized:\n" \ + " name: {cmdname}\n" \ + " args, kwargs: {args}, {kwargs}" + log_err(err.format(cmdname=cmdname, args=args, kwargs=kwargs)) #------------------------------------------------------------------------------------ diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index 81737900a3..46323f13de 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -372,7 +372,7 @@ class PortalSessionHandler(SessionHandler): try: getattr(session, "send_%s" % cmdname)(session, *cmdargs, **cmdkwargs) except AttributeError: - session.send_default(session, *cmdargs, **cmdkwargs) + session.send_default(session, cmdname, *cmdargs, **cmdkwargs) except Exception: log_trace() diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index a1258c0e33..9df0aef86b 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -71,11 +71,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): self.keep_alive = LoopingCall(self._write, IAC + NOP) self.keep_alive.start(30, now=False) - self.datamap = {"text": self.send_text, - "prompt": self.send_prompt, - "_default": self.send_oob} - - def handshake_done(self, force=False): """ This is called by all telnet extensions once they are finished. @@ -270,9 +265,9 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): """ self.sessionhandler.data_out(self, **kwargs) + # send_* methods - @staticmethod - def send_text(session, *args, **kwargs): + def send_text(self, *args, **kwargs): """ Send text data. This is an in-band telnet operation. @@ -301,14 +296,14 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): # handle arguments options = kwargs.get("options", {}) - ttype = session.protocol_flags.get('TTYPE', {}) + ttype = self.protocol_flags.get('TTYPE', {}) xterm256 = options.get("xterm256", ttype.get('256 COLORS', False) if ttype.get("init_done") else True) useansi = options.get("ansi", ttype and ttype.get('ANSI', False) if ttype.get("init_done") else True) raw = options.get("raw", False) nomarkup = options.get("nomarkup", not (xterm256 or useansi)) echo = options.get("echo", None) - mxp = options.get("mxp", session.protocol_flags.get("MXP", False)) - screenreader = options.get("screenreader", session.screenreader) + mxp = options.get("mxp", self.protocol_flags.get("MXP", False)) + screenreader = options.get("screenreader", self.screenreader) if screenreader: # screenreader mode cleans up output @@ -324,11 +319,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): prompt = mxp_parse(prompt) prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n') prompt += IAC + GA - session.transport.write(mccp_compress(session, prompt)) + self.transport.write(mccp_compress(self, prompt)) else: + if echo is not None: + # turn on/off echo + if echo: + self.transport.write(mccp_compress(self, IAC+WILL+ECHO)) + else: + self.transport.write(mccp_compress(self, IAC+WONT+ECHO)) if raw: # no processing - session.sendLine(text) + self.sendLine(text) return else: # we need to make sure to kill the color at the end in order @@ -336,29 +337,19 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256, mxp=mxp) if mxp: linetosend = mxp_parse(linetosend) - session.sendLine(linetosend) + self.sendLine(linetosend) - if echo is not None: - # turn on/off echo - if echo: - session.transport.write(mccp_compress(session, IAC+WILL+ECHO)) - else: - session.transport.write(mccp_compress(session, IAC+WONT+ECHO)) - - - @staticmethod - def send_prompt(session, *args, **kwargs): + def send_prompt(self, *args, **kwargs): """ Send a prompt - a text without a line end. See send_text for argument options. """ kwargs["options"].update({"send_prompt": True}) - session.send_text(*args, **kwargs) + self.send_text(*args, **kwargs) - @staticmethod - def send_oob(session, *args, **kwargs): + def send_default(self, cmdname, *args, **kwargs): """ - Send oob data + Send other oob data """ - print "telnet.send_oob not implemented yet! ", args + print "telnet.send_default not implemented yet! ", args diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index 482b13cc24..1baa0877ed 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -29,11 +29,6 @@ try: except ImportError: import pickle -# input handlers - -_INPUT_HANDLER_FUNCS = {} -for modname in make_iter(settings.INPUT_HANDLER_MODULES): - _INPUT_HANDLER_FUNCS.update(callables_from_module(modname)) # delayed imports _PlayerDB = None @@ -68,6 +63,13 @@ _MAX_SERVER_COMMANDS_PER_SECOND = 100.0 _MAX_SESSION_COMMANDS_PER_SECOND = 5.0 _MODEL_MAP = None +# input handlers + +_INPUT_HANDLER_FUNCS = {} +for modname in make_iter(settings.INPUT_HANDLER_MODULES): + print modname + _INPUT_HANDLER_FUNCS.update(callables_from_module(modname)) + def delayed_import(): """ Helper method for delayed import of all needed entities. @@ -610,7 +612,7 @@ class ServerSessionHandler(SessionHandler): print "sessionhandler: data_in", cmdname, cmdargs, cmdkwargs _INPUT_HANDLER_FUNCS[cmdname](session, *cmdargs, **cmdkwargs) else: - _INPUT_HANDLER_FUNCS["_default"](session, *cmdargs, **cmdkwargs) + _INPUT_HANDLER_FUNCS["default"](session, cmdname, *cmdargs, **cmdkwargs) except Exception: log_trace() diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index c24d067e5f..30c256a3a4 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -977,7 +977,7 @@ def all_from_module(module): return {} # make sure to only return variables actually defined in this module (not imports) members = getmembers(mod, predicate=lambda obj: getmodule(obj) == mod) - return dict((key, val) for key, val in members.iteritems() if not key.startswith("_")) + return dict((key, val) for key, val in members if not key.startswith("_")) #return dict((key, val) for key, val in mod.__dict__.items() # if not (key.startswith("_") or ismodule(val))) @@ -1002,7 +1002,7 @@ def callables_from_module(module): return {} # make sure to only return callables actually defined in this module (not imports) members = getmembers(mod, predicate=lambda obj: callable(obj) and getmodule(obj) == mod) - return dict((key, val) for key, val in members.iteritems() if not key.startswith("_")) + return dict((key, val) for key, val in members if not key.startswith("_")) def variable_from_module(module, variable=None, default=None):