Starting to clean up and debug the server-side infrastructure

This commit is contained in:
Griatch 2016-02-10 18:41:36 +01:00
parent e4d50ff74e
commit 96ace8c75f
9 changed files with 135 additions and 131 deletions

View file

@ -60,7 +60,7 @@ create_help_entry = None
# utilities # utilities
settings = None settings = None
lockfuncs = None lockfuncs = None
oobhandler = None inputhandler = None
logger = None logger = None
gametime = None gametime = None
ansi = None ansi = None
@ -167,7 +167,7 @@ def _init():
# handlers # handlers
from .scripts.tickerhandler import TICKER_HANDLER 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 .server.sessionhandler import SESSION_HANDLER
from .comms.channelhandler import CHANNEL_HANDLER from .comms.channelhandler import CHANNEL_HANDLER

View file

@ -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

View file

@ -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}

View file

@ -14,17 +14,16 @@ an object's properties or start a repeating action.
from builtins import object from builtins import object
from collections import defaultdict from collections import defaultdict
from django.conf import settings
from evennia.server.models import ServerConfig from evennia.server.models import ServerConfig
from evennia.server.sessionhandler import SESSIONS
from evennia.scripts.tickerhandler import TickerHandler from evennia.scripts.tickerhandler import TickerHandler
from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.utils import make_iter, mod_import
_SA = object.__setattr__ _SA = object.__setattr__
_GA = object.__getattribute__ _GA = object.__getattribute__
_DA = object.__delattr__ _DA = object.__delattr__
_SESSIONS = None
# set at the bottom of this module # set at the bottom of this module
_OOB_FUNCS = None _OOB_FUNCS = None
@ -66,14 +65,18 @@ class OOBFieldMonitor(object):
fieldname (str): The field to monitor 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(): for sessid, oobtuples in self.subscribers.items():
# oobtuples is a list [(oobfuncname, args, kwargs), ...], # oobtuples is a list [(oobfuncname, args, kwargs), ...],
# a potential list of oob commands to call when this # a potential list of oob commands to call when this
# field changes. # field changes.
sessid = SESSIONS.get(sessid) sessid = _SESSIONS.get(sessid)
if sessid: if sessid:
for (oobfuncname, args, kwargs) in oobtuples: 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): def add(self, session, oobfuncname, *args, **kwargs):
""" """
@ -123,7 +126,7 @@ class OOBAtRepeater(object):
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
"Called at regular intervals. Calls the oob function" "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 # Main OOB Handler
@ -456,30 +459,30 @@ class OOBHandler(TickerHandler):
# access object # access object
OOB_HANDLER = OOBHandler() 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
## 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
#

View file

@ -58,9 +58,10 @@ name.
from future.utils import viewkeys from future.utils import viewkeys
from django.conf import settings 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.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 _IDLE_COMMAND = settings.IDLE_COMMAND
_GA = object.__getattribute__ _GA = object.__getattribute__
@ -104,6 +105,16 @@ def text(session, *args, **kwargs):
cmdhandler(session, text, callertype="session", session=session) cmdhandler(session, text, callertype="session", session=session)
session.update_session_counters() 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))
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------

View file

@ -372,7 +372,7 @@ class PortalSessionHandler(SessionHandler):
try: try:
getattr(session, "send_%s" % cmdname)(session, *cmdargs, **cmdkwargs) getattr(session, "send_%s" % cmdname)(session, *cmdargs, **cmdkwargs)
except AttributeError: except AttributeError:
session.send_default(session, *cmdargs, **cmdkwargs) session.send_default(session, cmdname, *cmdargs, **cmdkwargs)
except Exception: except Exception:
log_trace() log_trace()

View file

@ -71,11 +71,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
self.keep_alive = LoopingCall(self._write, IAC + NOP) self.keep_alive = LoopingCall(self._write, IAC + NOP)
self.keep_alive.start(30, now=False) 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): def handshake_done(self, force=False):
""" """
This is called by all telnet extensions once they are finished. 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) self.sessionhandler.data_out(self, **kwargs)
# send_* methods
@staticmethod def send_text(self, *args, **kwargs):
def send_text(session, *args, **kwargs):
""" """
Send text data. This is an in-band telnet operation. Send text data. This is an in-band telnet operation.
@ -301,14 +296,14 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# handle arguments # handle arguments
options = kwargs.get("options", {}) 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) 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) useansi = options.get("ansi", ttype and ttype.get('ANSI', False) if ttype.get("init_done") else True)
raw = options.get("raw", False) raw = options.get("raw", False)
nomarkup = options.get("nomarkup", not (xterm256 or useansi)) nomarkup = options.get("nomarkup", not (xterm256 or useansi))
echo = options.get("echo", None) echo = options.get("echo", None)
mxp = options.get("mxp", session.protocol_flags.get("MXP", False)) mxp = options.get("mxp", self.protocol_flags.get("MXP", False))
screenreader = options.get("screenreader", session.screenreader) screenreader = options.get("screenreader", self.screenreader)
if screenreader: if screenreader:
# screenreader mode cleans up output # screenreader mode cleans up output
@ -324,11 +319,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
prompt = mxp_parse(prompt) prompt = mxp_parse(prompt)
prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n') prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
prompt += IAC + GA prompt += IAC + GA
session.transport.write(mccp_compress(session, prompt)) self.transport.write(mccp_compress(self, prompt))
else: 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: if raw:
# no processing # no processing
session.sendLine(text) self.sendLine(text)
return return
else: else:
# we need to make sure to kill the color at the end in order # 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) linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256, mxp=mxp)
if mxp: if mxp:
linetosend = mxp_parse(linetosend) linetosend = mxp_parse(linetosend)
session.sendLine(linetosend) self.sendLine(linetosend)
if echo is not None: def send_prompt(self, *args, **kwargs):
# 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):
""" """
Send a prompt - a text without a line end. See send_text for argument options. Send a prompt - a text without a line end. See send_text for argument options.
""" """
kwargs["options"].update({"send_prompt": True}) kwargs["options"].update({"send_prompt": True})
session.send_text(*args, **kwargs) self.send_text(*args, **kwargs)
@staticmethod def send_default(self, cmdname, *args, **kwargs):
def send_oob(session, *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

View file

@ -29,11 +29,6 @@ try:
except ImportError: except ImportError:
import pickle 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 # delayed imports
_PlayerDB = None _PlayerDB = None
@ -68,6 +63,13 @@ _MAX_SERVER_COMMANDS_PER_SECOND = 100.0
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0 _MAX_SESSION_COMMANDS_PER_SECOND = 5.0
_MODEL_MAP = None _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(): def delayed_import():
""" """
Helper method for delayed import of all needed entities. Helper method for delayed import of all needed entities.
@ -610,7 +612,7 @@ class ServerSessionHandler(SessionHandler):
print "sessionhandler: data_in", cmdname, cmdargs, cmdkwargs print "sessionhandler: data_in", cmdname, cmdargs, cmdkwargs
_INPUT_HANDLER_FUNCS[cmdname](session, *cmdargs, **cmdkwargs) _INPUT_HANDLER_FUNCS[cmdname](session, *cmdargs, **cmdkwargs)
else: else:
_INPUT_HANDLER_FUNCS["_default"](session, *cmdargs, **cmdkwargs) _INPUT_HANDLER_FUNCS["default"](session, cmdname, *cmdargs, **cmdkwargs)
except Exception: except Exception:
log_trace() log_trace()

View file

@ -977,7 +977,7 @@ def all_from_module(module):
return {} return {}
# make sure to only return variables actually defined in this module (not imports) # make sure to only return variables actually defined in this module (not imports)
members = getmembers(mod, predicate=lambda obj: getmodule(obj) == mod) 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() #return dict((key, val) for key, val in mod.__dict__.items()
# if not (key.startswith("_") or ismodule(val))) # if not (key.startswith("_") or ismodule(val)))
@ -1002,7 +1002,7 @@ def callables_from_module(module):
return {} return {}
# make sure to only return callables actually defined in this module (not imports) # 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) 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): def variable_from_module(module, variable=None, default=None):