mirror of
https://github.com/evennia/evennia.git
synced 2026-03-21 23:36:30 +01:00
Changes to the input-handling interface, to support function calls; also changing oob system to be inputhandler system, making it all work in the same way (no separation between text and oob anymore).
This commit is contained in:
parent
d966051558
commit
e4d50ff74e
7 changed files with 133 additions and 97 deletions
|
|
@ -59,16 +59,57 @@ from future.utils import viewkeys
|
|||
|
||||
from django.conf import settings
|
||||
from evennia.utils.utils import to_str
|
||||
from evennia.server.oobhandler import OOB_HANDLER
|
||||
from evennia.commands.cmdhandler import cmdhandler
|
||||
#from evennia.server.inputhandler import INPUT_HANDLER
|
||||
|
||||
_IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
_GA = object.__getattribute__
|
||||
_SA = object.__setattr__
|
||||
_NA = lambda o: "N/A"
|
||||
|
||||
def text(session, *args, **kwargs):
|
||||
"""
|
||||
Main text input from the client. This will execute a command
|
||||
string on the server.
|
||||
|
||||
Args:
|
||||
text (str): First arg is used as text-command input. Other
|
||||
arguments are ignored.
|
||||
|
||||
"""
|
||||
#from evennia.server.profiling.timetrace import timetrace
|
||||
#text = timetrace(text, "ServerSession.data_in")
|
||||
|
||||
text = args[0] if args else None
|
||||
|
||||
#explicitly check for None since text can be an empty string, which is
|
||||
#also valid
|
||||
if text is None:
|
||||
return
|
||||
# this is treated as a command input
|
||||
#text = to_unicode(escape_control_sequences(text), encoding=self.encoding)
|
||||
# handle the 'idle' command
|
||||
if text.strip() == _IDLE_COMMAND:
|
||||
session.update_session_counters(idle=True)
|
||||
return
|
||||
if session.player:
|
||||
# nick replacement
|
||||
puppet = session.puppet
|
||||
if puppet:
|
||||
text = puppet.nicks.nickreplace(text,
|
||||
categories=("inputline", "channel"), include_player=True)
|
||||
else:
|
||||
text = session.player.nicks.nickreplace(text,
|
||||
categories=("inputline", "channels"), include_player=False)
|
||||
cmdhandler(session, text, callertype="session", session=session)
|
||||
session.update_session_counters()
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------------
|
||||
|
||||
#------------------------------------------------------------
|
||||
# All OOB commands must be on the form
|
||||
# cmdname(oobhandler, session, *args, **kwargs)
|
||||
# cmdname(session, *args, **kwargs)
|
||||
#------------------------------------------------------------
|
||||
|
||||
#
|
||||
|
|
@ -126,7 +167,7 @@ def oob_repeat(session, oobfuncname, interval, *args, **kwargs):
|
|||
interval = 20 if not interval else (max(5, interval))
|
||||
obj = session.get_puppet_or_player()
|
||||
if obj and oobfuncname != "REPEAT":
|
||||
OOB_HANDLER.add_repeater(obj, session, oobfuncname, interval, *args, **kwargs)
|
||||
INPUT_HANDLER.add_repeater(obj, session, oobfuncname, interval, *args, **kwargs)
|
||||
|
||||
|
||||
##OOB{"UNREPEAT":10}
|
||||
|
|
@ -147,7 +188,7 @@ def oob_unrepeat(session, oobfuncname, interval):
|
|||
"""
|
||||
obj = session.get_puppet_or_player()
|
||||
if obj:
|
||||
OOB_HANDLER.remove_repeater(obj, session, oobfuncname, interval)
|
||||
INPUT_HANDLER.remove_repeater(obj, session, oobfuncname, interval)
|
||||
|
||||
|
||||
#
|
||||
|
|
@ -165,7 +206,7 @@ def oob_unrepeat(session, oobfuncname, interval):
|
|||
|
||||
|
||||
# mapping from MSDP standard names to Evennia variables
|
||||
OOB_SENDABLE = {
|
||||
_OOB_SENDABLE = {
|
||||
"CHARACTER_NAME": lambda o: o.key,
|
||||
"SERVER_ID": lambda o: settings.SERVERNAME,
|
||||
"ROOM_NAME": lambda o: o.db_location.key,
|
||||
|
|
@ -210,7 +251,7 @@ def oob_send(session, *args, **kwargs):
|
|||
|
||||
|
||||
# mapping standard MSDP keys to Evennia field names
|
||||
OOB_REPORTABLE = {
|
||||
_OOB_REPORTABLE = {
|
||||
"CHARACTER_NAME": "db_key",
|
||||
"ROOM_NAME": "db_location",
|
||||
"TEST" : "test"
|
||||
|
|
@ -252,10 +293,10 @@ def oob_report(session, *args, **kwargs):
|
|||
oob_error(session, "No Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % propname)
|
||||
# the field_monitors require an oob function as a callback when they report a change.
|
||||
elif propname.startswith("db_"):
|
||||
OOB_HANDLER.add_field_monitor(obj, session, propname, "return_field_report")
|
||||
INPUT_HANDLER.add_field_monitor(obj, session, propname, "return_field_report")
|
||||
ret.append(to_str(_GA(obj, propname), force_string=True))
|
||||
else:
|
||||
OOB_HANDLER.add_attribute_monitor(obj, session, propname, "return_attribute_report")
|
||||
INPUT_HANDLER.add_attribute_monitor(obj, session, propname, "return_attribute_report")
|
||||
ret.append(_GA(obj, "db_value"))
|
||||
session.msg(oob=("MSDP_ARRAY", ret))
|
||||
else:
|
||||
|
|
@ -314,9 +355,9 @@ def oob_unreport(session, *args, **kwargs):
|
|||
if not propname:
|
||||
oob_error(session, "No Un-Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % propname)
|
||||
elif propname.startswith("db_"):
|
||||
OOB_HANDLER.remove_field_monitor(obj, session, propname, "oob_return_field_report")
|
||||
INPUT_HANDLER.remove_field_monitor(obj, session, propname, "oob_return_field_report")
|
||||
else: # assume attribute
|
||||
OOB_HANDLER.remove_attribute_monitor(obj, session, propname, "oob_return_attribute_report")
|
||||
INPUT_HANDLER.remove_attribute_monitor(obj, session, propname, "oob_return_attribute_report")
|
||||
else:
|
||||
oob_error(session, "You must log in first.")
|
||||
|
||||
|
|
@ -359,7 +400,7 @@ def oob_list(session, mode, *args, **kwargs):
|
|||
# we need to check so as to use the right return value depending on if it is
|
||||
# an Attribute (identified by tracking the db_value field) or a normal database field
|
||||
# reported is a list of tuples (obj, propname, args, kwargs)
|
||||
reported = OOB_HANDLER.get_all_monitors(session)
|
||||
reported = INPUT_HANDLER.get_all_monitors(session)
|
||||
reported = [rep[0].key if rep[1] == "db_value" else rep[1] for rep in reported]
|
||||
session.msg(oob=("REPORTED_VARIABLES", reported))
|
||||
elif mode == "SENDABLE_VARIABLES":
|
||||
|
|
@ -381,17 +422,17 @@ def oob_list(session, mode, *args, **kwargs):
|
|||
# this maps the commands to the names available to use from
|
||||
# the oob call. The standard MSDP commands are capitalized
|
||||
# as per the protocol, Evennia's own commands are not.
|
||||
CMD_MAP = {"oob_error": oob_error, # will get error messages
|
||||
"return_field_report": oob_return_field_report,
|
||||
"return_attribute_report": oob_return_attribute_report,
|
||||
# MSDP
|
||||
"REPEAT": oob_repeat,
|
||||
"UNREPEAT": oob_unrepeat,
|
||||
"SEND": oob_send,
|
||||
"ECHO": oob_echo,
|
||||
"REPORT": oob_report,
|
||||
"UNREPORT": oob_unreport,
|
||||
"LIST": oob_list,
|
||||
# GMCP
|
||||
}
|
||||
#CMD_MAP = {"oob_error": oob_error, # will get error messages
|
||||
# "return_field_report": oob_return_field_report,
|
||||
# "return_attribute_report": oob_return_attribute_report,
|
||||
# # MSDP
|
||||
# "REPEAT": oob_repeat,
|
||||
# "UNREPEAT": oob_unrepeat,
|
||||
# "SEND": oob_send,
|
||||
# "ECHO": oob_echo,
|
||||
# "REPORT": oob_report,
|
||||
# "UNREPORT": oob_unreport,
|
||||
# "LIST": oob_list,
|
||||
# # GMCP
|
||||
# }
|
||||
|
||||
|
|
@ -356,8 +356,10 @@ class PortalSessionHandler(SessionHandler):
|
|||
session (Session): Session sending data.
|
||||
|
||||
Kwargs:
|
||||
text (str): Text from protocol.
|
||||
kwargs (any): Other data from protocol.
|
||||
kwargs (any): Each key is a command instruction to the
|
||||
protocol on the form key = [[args],{kwargs}]. This will
|
||||
call a method send_<key> on the protocol. If no such
|
||||
method exixts, it send the data to a method send_default.
|
||||
|
||||
"""
|
||||
#from evennia.server.profiling.timetrace import timetrace
|
||||
|
|
@ -365,13 +367,12 @@ class PortalSessionHandler(SessionHandler):
|
|||
|
||||
# distribute outgoing data to the correct session methods.
|
||||
if session:
|
||||
print ("portalsessionhandler.data_out:", session, kwargs, session.datamap)
|
||||
for cmdname, args in kwargs.items():
|
||||
print ("portalsessionhandler.data_out:", session, kwargs)
|
||||
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
||||
try:
|
||||
if cmdname in session.datamap:
|
||||
session.datamap[cmdname](session, *args)
|
||||
else:
|
||||
session.datamap["_default"](session, *args)
|
||||
getattr(session, "send_%s" % cmdname)(session, *cmdargs, **cmdkwargs)
|
||||
except AttributeError:
|
||||
session.send_default(session, *cmdargs, **cmdkwargs)
|
||||
except Exception:
|
||||
log_trace()
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class WebSocketClient(Protocol, Session):
|
|||
Method called when data is coming in over the websocket
|
||||
connection. This is always a JSON object on the following
|
||||
form:
|
||||
[cmdname, arg, arg2, ...]
|
||||
[cmdname, [args], {kwargs}]
|
||||
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ from evennia.utils import logger
|
|||
from evennia.utils.inlinefunc import parse_inlinefunc
|
||||
from evennia.utils.nested_inlinefuncs import parse_inlinefunc as parse_nested_inlinefunc
|
||||
from evennia.utils.utils import make_iter, lazy_property
|
||||
from evennia.commands.cmdhandler import cmdhandler
|
||||
from evennia.commands.cmdsethandler import CmdSetHandler
|
||||
from evennia.server.session import Session
|
||||
|
||||
_IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
_GA = object.__getattribute__
|
||||
_SA = object.__setattr__
|
||||
_ObjectDB = None
|
||||
|
|
@ -32,7 +30,6 @@ _INLINEFUNC_ENABLED = settings.INLINEFUNC_ENABLED
|
|||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
# Handlers for Session.db/ndb operation
|
||||
|
||||
class NDbHolder(object):
|
||||
|
|
@ -168,8 +165,6 @@ class ServerSession(Session):
|
|||
self.player = None
|
||||
self.cmdset_storage_string = ""
|
||||
self.cmdset = CmdSetHandler(self, True)
|
||||
self.datamap = {"text": self.recv_text,
|
||||
"_default": self.recv_text}
|
||||
|
||||
def __cmdset_storage_get(self):
|
||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||
|
|
@ -337,42 +332,6 @@ class ServerSession(Session):
|
|||
# Player-visible idle time, not used in idle timeout calcs.
|
||||
self.cmd_last_visible = self.cmd_last
|
||||
|
||||
@staticmethod
|
||||
def recv_text(session, *args, **kwargs):
|
||||
"""
|
||||
Recv command data User->Evennia. This will in effect execute a command
|
||||
string on the server.
|
||||
|
||||
Args:
|
||||
text (str): First arg is used as text-command input. Other
|
||||
arguments are ignored.
|
||||
|
||||
"""
|
||||
#from evennia.server.profiling.timetrace import timetrace
|
||||
#text = timetrace(text, "ServerSession.data_in")
|
||||
|
||||
text = args[0] if args else None
|
||||
|
||||
#explicitly check for None since text can be an empty string, which is
|
||||
#also valid
|
||||
if text is not None:
|
||||
# this is treated as a command input
|
||||
#text = to_unicode(escape_control_sequences(text), encoding=self.encoding)
|
||||
# handle the 'idle' command
|
||||
if text.strip() == _IDLE_COMMAND:
|
||||
session.update_session_counters(idle=True)
|
||||
return
|
||||
if session.player:
|
||||
# nick replacement
|
||||
puppet = session.puppet
|
||||
if puppet:
|
||||
text = puppet.nicks.nickreplace(text,
|
||||
categories=("inputline", "channel"), include_player=True)
|
||||
else:
|
||||
text = session.player.nicks.nickreplace(text,
|
||||
categories=("inputline", "channels"), include_player=False)
|
||||
cmdhandler(session, text, callertype="session", session=session)
|
||||
session.update_session_counters()
|
||||
|
||||
def data_out(self, text=None, **kwargs):
|
||||
"""
|
||||
|
|
@ -384,6 +343,11 @@ class ServerSession(Session):
|
|||
by their keys. Or "options", carrying options
|
||||
for the protocol(s).
|
||||
|
||||
Notes:
|
||||
We need to handle inlinefunc-parsing at this point
|
||||
since we must have access to the database and the
|
||||
Server Session.
|
||||
|
||||
"""
|
||||
print "serversession.data_out:", text, kwargs
|
||||
if text:
|
||||
|
|
@ -392,7 +356,7 @@ class ServerSession(Session):
|
|||
else:
|
||||
text, args = text, []
|
||||
print("kwargs", kwargs, kwargs.get("options", {}))
|
||||
options = kwargs.get("options", None) or {}
|
||||
options = kwargs.get("options", [None, {}])[1]
|
||||
raw = options.get("raw", False)
|
||||
strip_inlinefunc = options.get("strip_inlinefunc", False)
|
||||
if _INLINEFUNC_ENABLED and not raw:
|
||||
|
|
|
|||
|
|
@ -19,14 +19,22 @@ from time import time
|
|||
from django.conf import settings
|
||||
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||
from evennia.utils.logger import log_trace
|
||||
from evennia.utils.utils import variable_from_module, is_iter, \
|
||||
to_str, to_unicode, strip_control_sequences, make_iter
|
||||
from evennia.utils.utils import (variable_from_module, is_iter,
|
||||
to_str, to_unicode,
|
||||
make_iter,
|
||||
callables_from_module)
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
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
|
||||
_ServerSession = None
|
||||
|
|
@ -123,15 +131,24 @@ class SessionHandler(dict):
|
|||
|
||||
Args:
|
||||
session (Session): The relevant session instance.
|
||||
kwargs (dict): Every keyword represents a send-instruction.
|
||||
kwargs (dict) Each keyword represents a
|
||||
send-instruction, with the keyword itself being the name
|
||||
of the instruction (like "text"). Suitable values for each
|
||||
keyword are:
|
||||
- arg -> [[arg], {}]
|
||||
- [args] -> [[args], {}]
|
||||
- {kwargs} -> [[], {kwargs}]
|
||||
- [args, {kwargs}] -> [[arg], {kwargs}]
|
||||
- [[args], {kwargs}] -> [[args], {kwargs}]
|
||||
|
||||
Returns:
|
||||
kwargs (dict): A cleaned dictionary of cmdname:args pairs,
|
||||
where the keys and args have all been converted to
|
||||
kwargs (dict): A cleaned dictionary of cmdname:[[args],{kwargs}] pairs,
|
||||
where the keys, args and kwargs have all been converted to
|
||||
send-safe entities (strings or numbers).
|
||||
|
||||
"""
|
||||
def _validate(data):
|
||||
"Helper function to convert data to AMP-safe (picketable) values"
|
||||
if isinstance(data, dict):
|
||||
newdict = {}
|
||||
for key, part in data.items():
|
||||
|
|
@ -140,23 +157,36 @@ class SessionHandler(dict):
|
|||
elif hasattr(data, "__iter__"):
|
||||
return [_validate(part) for part in data]
|
||||
elif isinstance(data, basestring):
|
||||
# make sure strings are in a valid encoding
|
||||
try:
|
||||
return data and to_str(to_unicode(data), encoding=session.encoding)
|
||||
except LookupError:
|
||||
# wrong encoding set on the session. Set it to a safe one
|
||||
session.encoding = "utf-8"
|
||||
return to_str(to_unicode(data), encoding=session.encoding)
|
||||
elif hasattr(data, "id") and hasattr(data, "db_date_created") and hasattr(data, '__dbclass__'):
|
||||
elif hasattr(data, "id") and hasattr(data, "db_date_created") \
|
||||
and hasattr(data, '__dbclass__'):
|
||||
# convert database-object to their string representation.
|
||||
return _validate(unicode(data))
|
||||
else:
|
||||
return data
|
||||
clean_kwargs = {"options":kwargs.pop("options", {})}
|
||||
for key in kwargs:
|
||||
args = _validate(kwargs[key])
|
||||
clean_kwargs[_validate(key)] = (args,) if args is not None and \
|
||||
not hasattr(args, "__iter__") else args
|
||||
return clean_kwargs
|
||||
|
||||
rkwargs = {}
|
||||
for key, data in kwargs.iteritems():
|
||||
if not data:
|
||||
rkwargs[key] = [ [], {} ]
|
||||
elif isinstance(data, dict):
|
||||
rkwargs[key] = [ [], _validate(data) ]
|
||||
elif hasattr(data, "__iter__"):
|
||||
if isinstance(data[-1], dict):
|
||||
# last entry is a kwarg dict
|
||||
rkwargs[key] = [ _validate(data[:-1]), _validate(data[-1]) ]
|
||||
else:
|
||||
rkwargs[key] = [ _validate(data), {} ]
|
||||
else:
|
||||
rkwargs[key] = [ [_validate(data)], {} ]
|
||||
|
||||
return rkwargs
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -574,13 +604,13 @@ class ServerSessionHandler(SessionHandler):
|
|||
|
||||
# distribute incoming data to the correct receiving methods.
|
||||
if session:
|
||||
for cmdname, args in kwargs.items():
|
||||
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
||||
try:
|
||||
if cmdname in session.datamap:
|
||||
print "sessionhandler: data_in", cmdname, args
|
||||
session.datamap[cmdname](session, *args)
|
||||
if cmdname in _INPUT_HANDLER_FUNCS:
|
||||
print "sessionhandler: data_in", cmdname, cmdargs, cmdkwargs
|
||||
_INPUT_HANDLER_FUNCS[cmdname](session, *cmdargs, **cmdkwargs)
|
||||
else:
|
||||
session.datamap["_default"](session, *args)
|
||||
_INPUT_HANDLER_FUNCS["_default"](session, *cmdargs, **cmdkwargs)
|
||||
except Exception:
|
||||
log_trace()
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ TELNET_INTERFACES = ['0.0.0.0']
|
|||
# special commands and data with enabled Telnet clients. This is used
|
||||
# to create custom client interfaces over a telnet connection. To make
|
||||
# full use of OOB, you need to prepare functions to handle the data
|
||||
# server-side (see OOB_FUNC_MODULE). TELNET_ENABLED is required for this
|
||||
# server-side (see INPUT_HANDLER_MODULES). TELNET_ENABLED is required for this
|
||||
# to work.
|
||||
TELNET_OOB_ENABLED = False
|
||||
# Start the evennia django+twisted webserver so you can
|
||||
|
|
@ -276,10 +276,10 @@ MSSP_META_MODULE = "server.conf.mssp"
|
|||
# Tuple of modules implementing lock functions. All callable functions
|
||||
# inside these modules will be available as lock functions.
|
||||
LOCK_FUNC_MODULES = ("evennia.locks.lockfuncs", "server.conf.lockfuncs",)
|
||||
# Module holding OOB (Out of Band) hook objects. This allows for customization
|
||||
# and expansion of which hooks OOB protocols are allowed to call on the server
|
||||
# protocols for attaching tracker hooks for when various object field change
|
||||
OOB_PLUGIN_MODULES = ["evennia.server.oob_cmds", "server.conf.oobfuncs"]
|
||||
# Module holding handlers for managing incoming data from the client. These
|
||||
# will be loaded in order, meaning functions in later modules may overload
|
||||
# previous ones if having the same name.
|
||||
INPUT_HANDLER_MODULES = ["evennia.server.inputhandler_funcs", "server.conf.inputhandler_funcs"]
|
||||
# Module holding settings/actions for the dummyrunner program (see the
|
||||
# dummyrunner for more information)
|
||||
DUMMYRUNNER_SETTINGS_MODULE = "evennia.server.profiling.dummyrunner_settings"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue