Implemented GMCP with the new oobhandler. Resolves #208.

This commit is contained in:
Griatch 2015-02-14 19:46:25 +01:00
parent 00aa28004c
commit 08525f11ee
8 changed files with 172 additions and 144 deletions

View file

@ -79,7 +79,7 @@ class Ticker(object):
appropriately.
"""
for key, (obj, args, kwargs) in self.subscriptions.items():
hook_key = yield kwargs.get("hook_key", "at_tick")
hook_key = yield kwargs.get("_hook_key", "at_tick")
if not obj:
# object was deleted between calls
self.validate()
@ -256,21 +256,48 @@ class TickerHandler(object):
def add(self, obj, interval, idstring="", hook_key="at_tick", *args, **kwargs):
"""
Add object to tickerhandler. The object must have an at_tick
method. This will be called every interval seconds until the
object is unsubscribed from the ticker.
Add object to tickerhandler
Args:
obj (Object): The object to subscribe to the ticker.
interval (int): Interval in seconds between calling
`hook_key` below.
idstring (str, optional): Identifier for separating
this ticker-subscription from others with the same
interval. Allows for managing multiple calls with
the same time interval
hook_key (str, optional): The name of the hook method
on `obj` to call every `interval` seconds. Defaults to
`at_tick()`.
args, kwargs (optional): These will be passed into the
method given by `hook_key` every time it is called.
Notes:
The combination of `obj`, `interval` and `idstring`
together uniquely defines the ticker subscription. They
must all be supplied in order to unsubscribe from it
later.
"""
isdb, store_key = self._store_key(obj, interval, idstring)
if isdb:
self.ticker_storage[store_key] = (args, kwargs)
self.save()
kwargs["hook_key"] = hook_key
kwargs["_hook_key"] = hook_key
self.ticker_pool.add(store_key, obj, interval, *args, **kwargs)
def remove(self, obj, interval=None, idstring=""):
"""
Remove object from ticker, or only this object ticking
at a given interval.
Remove object from ticker or only remove it from tickers with
a given interval.
Args:
obj (Object): The object subscribing to the ticker.
interval (int, optional): Interval of ticker to remove. If
`None`, all tickers on this object matching `idstring`
will be removed, regardless of their `interval` setting.
idstring (str, optional): Identifier id of ticker to remove.
"""
if interval:
isdb, store_key = self._store_key(obj, interval, idstring)

View file

@ -373,10 +373,10 @@ class AMPProtocol(amp.AMP):
"""
Relays message to Portal. This method is executed on the Portal.
"""
#print "msg server->portal (portal side):", sessid, msg
ret = self.safe_recv(MsgServer2Portal, sessid,
ipart, nparts, text=msg, data=data)
if ret is not None:
print "msg server->portal (portal side):", sessid, ret["text"], loads(ret["data"])
self.factory.portal.sessions.data_out(sessid,
text=ret["text"],
**loads(ret["data"]))

View file

@ -89,7 +89,7 @@ def oob_error(oobhandler, session, errmsg, *args, **kwargs):
management.
"""
session.msg(oob=("error", ("OOB ERROR: %s" % errmsg)))
session.msg(oob=("error", ("OOB ERROR: %s" % errmsg,)))
def oob_echo(oobhandler, session, *args, **kwargs):
"""
@ -107,7 +107,7 @@ def oob_echo(oobhandler, session, *args, **kwargs):
##OOB{"repeat":10}
def oob_repeat(oobhandler, session, oobfuncname, interval, *args, **kwargs):
"""
Called as REPEAT <oobfunc> <interval>
Called as REPEAT <oobfunc> <interval> <args>
Repeats a given OOB command with a certain frequency.
Args:
@ -120,8 +120,14 @@ def oob_repeat(oobhandler, session, oobfuncname, interval, *args, **kwargs):
The command checks so that it cannot repeat itself.
"""
if oobfuncname != "REPEAT":
oobhandler.add_repeat(None, session.sessid, oobfuncname, interval, *args, **kwargs)
if not oobfuncname:
oob_error(oobhandler, session, "Usage: REPEAT <oobfuncname>, <interval>")
return
# limit repeat actions to minimum 5 seconds interval
interval = 20 if not interval else (max(5, interval))
obj = session.get_puppet_or_player()
if obj and oobfuncname != "REPEAT":
oobhandler.add_repeater(obj, session.sessid, oobfuncname, interval, *args, **kwargs)
##OOB{"UNREPEAT":10}
@ -132,16 +138,18 @@ def oob_unrepeat(oobhandler, session, oobfuncname, interval):
Args:
oobhandler (OOBHandler): main OOB handler.
session (Session): Session controlling the repeat
session (Session): Session controlling the repeater
oobfuncname (str): OOB function called every interval seconds
interval (int): Interval of repeat, in seconds.
interval (int): Interval of repeater, in seconds.
Notes:
The command checks so that it cannot repeat itself.
"""
oobhandler.remove_repeat(None, session.sessid, oobfuncname, interval)
obj = session.get_puppet_or_player()
if obj:
oobhandler.remove_repeater(obj, session.sessid, oobfuncname, interval)
#
@ -194,7 +202,7 @@ def oob_send(oobhandler, session, *args, **kwargs):
if obj:
for name in (a.upper() for a in args if a):
try:
print "MSDP SEND inp:", name
#print "MSDP SEND inp:", name
value = OOB_SENDABLE.get(name, _NA)(obj)
ret[name] = value
except Exception, e:
@ -253,7 +261,7 @@ def oob_report(oobhandler, session, *args, **kwargs):
else:
oobhandler.add_attribute_monitor(obj, session.sessid, propname, "return_attribute_report")
ret.append(_GA(obj, "db_value"))
print "ret:", ret
#print "ret:", ret
session.msg(oob=("MSDP_ARRAY", ret))
else:
oob_error(oobhandler, session, "You must log in first.")
@ -336,11 +344,6 @@ def oob_list(oobhandler, session, mode, *args, **kwargs):
"UNREPORT",
# "RESET",
"SEND")))
elif mode == "LISTS":
session.msg(oob=("LISTS",("REPORTABLE_VARIABLES",
"REPORTED_VARIABLES",
# "CONFIGURABLE_VARIABLES",
"SENDABLE_VARIABLES")))
elif mode == "REPORTABLE_VARIABLES":
session.msg(oob=("REPORTABLE_VARIABLES", tuple(key for key in OOB_REPORTABLE.keys())))
elif mode == "REPORTED_VARIABLES":
@ -356,7 +359,11 @@ def oob_list(oobhandler, session, mode, *args, **kwargs):
# Not implemented (game specific)
oob_error(oobhandler, session, "Not implemented (game specific)")
else:
oob_error(oobhandler, session, "Unsupported mode")
# mode == "LISTS" or not given
session.msg(oob=("LISTS",("REPORTABLE_VARIABLES",
"REPORTED_VARIABLES",
# "CONFIGURABLE_VARIABLES",
"SENDABLE_VARIABLES")))
#
# Cmd mapping
@ -368,8 +375,8 @@ def oob_list(oobhandler, session, mode, *args, **kwargs):
CMD_MAP = {"oob_error": oob_error, # will get error messages
"return_field_report": oob_return_field_report,
"return_attribute_report": oob_return_attribute_report,
"repeat": oob_repeat,
"unrepeat": oob_unrepeat,
"REPEAT": oob_repeat,
"UNREPEAT": oob_unrepeat,
"SEND": oob_send,
"ECHO": oob_echo,
"REPORT": oob_report,

View file

@ -1,36 +1,14 @@
"""
OOBHandler - Out Of Band Handler
The OOBHandler.execute_cmd is called by the sessionhandler when it detects
an OOB instruction (exactly how this looked depends on the protocol; at this
point all oob calls should look the same)
The OOBHandler.execute_cmd is called by the sessionhandler when it
detects an `oob` keyword in the outgoing data (usually called via
`msg(oob=...)`
The handler pieces of functionality:
function execution - the oob protocol can execute a function directly on
the server. The available functions must be defined
as global functions in settings.OOB_PLUGIN_MODULES.
repeat func execution - the oob protocol can request a given function be
executed repeatedly at a regular interval. This
uses an internal script pool.
tracking - the oob protocol can request Evennia to track changes to
fields on objects, as well as changes in Attributes. This is
done by dynamically adding tracker-objects on entities. The
behaviour of those objects can be customized by adding new
tracker classes in settings.OOB_PLUGIN_MODULES.
What goes into the OOB_PLUGIN_MODULES is a (list of) modules that contains
the working server-side code available to the OOB system: oob functions and
tracker classes.
oob functions have the following call signature:
function(caller, session, *args, **kwargs)
oob trackers should inherit from the OOBTracker class (in this
module) and implement a minimum of the same functionality.
If a function named "oob_error" is given, this will be called with error
messages.
How this works is that the handler executes an oobfunction, which is
defined in a user-supplied module. This function can then make use of
the oobhandler's functionality to return data, register a monitor on
an object's properties or start a repeating action.
"""
@ -132,17 +110,21 @@ class OOBFieldMonitor(object):
self.subscribers.pop(sessid, None)
class OOBAtRepeat(object):
class OOBAtRepeater(object):
"""
This object should be stored on a target object, named
as the hook to call repeatedly, e.g.
This object is created and used by the `OOBHandler.repeat` method.
It will be assigned to a target object as a custom variable, e.g.:
`obj._oob_ECHO_every_20s_for_sessid_1 = AtRepater()`
It will be called every interval seconds by the OOBHandler,
triggering whatever OOB function it is set to use.
_oob_listen_every_20s_for_sessid_1 = AtRepat()
"""
def __call__(self, sessid, oobfuncname, *args, **kwargs):
def __call__(self, *args, **kwargs):
"Called at regular intervals. Calls the oob function"
OOB_HANDLER.execute_cmd(sessid, oobfuncname, *args, **kwargs)
OOB_HANDLER.execute_cmd(kwargs["_sessid"], kwargs["_oobfuncname"], *args, **kwargs)
# Main OOB Handler
@ -153,13 +135,13 @@ class OOBHandler(TickerHandler):
"""
def __init__(self, *args, **kwargs):
super(OOBHandler, self).__init__(*args, **kwargs)
self.save_name = "oob_ticker_storage"
self.oob_save_name = "oob_monitor_storage"
self.oob_monitor_storage = {}
super(OOBHandler, self).__init__(*args, **kwargs)
def _get_repeat_hook_name(self, oobfuncname, interval, sessid):
"Return the unique repeat call hook name for this object"
def _get_repeater_hook_name(self, oobfuncname, interval, sessid):
"Return the unique repeater call hook name for this object"
return "_oob_%s_every_%ss_for_sessid_%s" % (oobfuncname, interval, sessid)
def _get_fieldmonitor_name(self, fieldname):
@ -220,7 +202,7 @@ class OOBHandler(TickerHandler):
by the Server process as part of the reload upstart. Here we
overload the tickerhandler's restore method completely to make
sure we correctly re-apply and re-initialize the correct
monitor and repeat objects on all saved objects.
monitor and repeater objecth on all saved objects.
"""
# load the oob monitors and initialize them
oob_storage = ServerConfig.objects.conf(key=self.oob_save_name)
@ -232,7 +214,7 @@ class OOBHandler(TickerHandler):
obj = unpack_dbobj(obj)
self._add_monitor(obj, sessid, fieldname, oobfuncname, *args, **kwargs)
# handle the tickers (same as in TickerHandler except we call
# the add_repeat method which makes sure to add the hooks before
# the add_repeater method which makes sure to add the hooks before
# starting the tickerpool)
ticker_storage = ServerConfig.objects.conf(key=self.save_name)
if ticker_storage:
@ -240,12 +222,12 @@ class OOBHandler(TickerHandler):
for store_key, (args, kwargs) in self.ticker_storage.items():
obj, interval, idstring = store_key
obj = unpack_dbobj(obj)
# we saved these in add_repeat before, can now retrieve them
sessid = kwargs["sessid"]
oobfuncname = kwargs["oobfuncname"]
self.add_repeat(obj, sessid, oobfuncname, interval, *args, **kwargs)
# we saved these in add_repeater before, can now retrieve them
sessid = kwargs["_sessid"]
oobfuncname = kwargs["_oobfuncname"]
self.add_repeater(obj, sessid, oobfuncname, interval, *args, **kwargs)
def add_repeat(self, obj, sessid, oobfuncname, interval=20, *args, **kwargs):
def add_repeater(self, obj, sessid, oobfuncname, interval=20, *args, **kwargs):
"""
Set an oob function to be repeatedly called.
@ -261,22 +243,20 @@ class OOBHandler(TickerHandler):
if not isinstance(sessid, int):
sessid = sessid.sessid
hook = OOBAtRepeat()
hookname = self._get_repeat_hook_name(oobfuncname, interval, sessid)
hook = OOBAtRepeater()
hookname = self._get_repeater_hook_name(oobfuncname, interval, sessid)
_SA(obj, hookname, hook)
kwargs.update({"sessid":sessid, "oobfuncname":oobfuncname})
# we store these in kwargs so that tickerhandler saves them with the rest
kwargs["sessid"] = sessid
kwargs["oobfuncbame"] = oobfuncname
self.add(obj, interval, idstring=oobfuncname, hook_key=hookname, *args, **kwargs)
kwargs.update({"_sessid":sessid, "_oobfuncname":oobfuncname})
super(OOBHandler, self).add(obj, int(interval), oobfuncname, hookname, *args, **kwargs)
def remove_repeat(self, obj, sessid, oobfuncname, interval=20):
def remove_repeater(self, obj, sessid, oobfuncname, interval=20):
"""
Remove the repeatedly calling oob function
Args:
obj (Object): The object on which the repeater sits
sessid (int): Session id of the Session that registered the repeat
sessid (int): Session id of the Session that registered the repeater
oobfuncname (str): Name of oob function to call at repeat
interval (int, optional): Number of seconds between repeats
@ -284,8 +264,8 @@ class OOBHandler(TickerHandler):
# check so we didn't get a session instead of a sessid
if not isinstance(sessid, int):
sessid = sessid.sessid
self.remove(obj, interval, idstring=oobfuncname)
hookname = self._get_repeat_hook_name(oobfuncname, interval, sessid)
super(OOBHandler, self).remove(obj, interval, idstring=oobfuncname)
hookname = self._get_repeater_hook_name(oobfuncname, interval, sessid)
try:
_DA(obj, hookname)
except AttributeError:
@ -406,8 +386,8 @@ class OOBHandler(TickerHandler):
oobfuncname (str): The name of the oob command (case sensitive)
Notes:
If the oobfuncname is a valid oob function, the `*args` and
`**kwargs` are passed into the oob command.
If the oobfuncname is a valid oob function, `args` and
`kwargs` are passed into the oob command.
"""
if isinstance(session, int):
@ -418,13 +398,19 @@ class OOBHandler(TickerHandler):
(session, oobfuncname, args, kwargs)
raise RuntimeError(errmsg)
print "execute_oob:", session, oobfuncname, args, kwargs
# don't catch this, wrong oobfuncname should be reported
oobfunc = _OOB_FUNCS[oobfuncname]
#print "execute_oob:", session, oobfuncname, args, kwargs
try:
oobfunc = _OOB_FUNCS[oobfuncname]
except Exception:
errmsg = "'%s' is not a valid OOB command. Commands available:\n %s" % (oobfuncname, ", ".join(_OOB_FUNCS))
if _OOB_ERROR:
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
errmsg = "OOB ERROR: %s" % errmsg
logger.log_trace(errmsg)
raise
# we found an oob command. Execute it.
try:
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
oobfunc(self, session, *args, **kwargs)
except Exception, err:
errmsg = "Exception in %s(*%s, **%s):\n%s" % (oobfuncname, args, kwargs, err)

View file

@ -191,17 +191,22 @@ class PortalSessionHandler(SessionHandler):
Helper method for each session to use to parse oob structures
(The 'oob' kwarg of the msg() method).
Allowed input oob structures are:
cmdname
((cmdname,), (cmdname,))
(cmdname,(arg, ))
(cmdname,(arg1,arg2))
(cmdname,{key:val,key2:val2})
(cmdname, (args,), {kwargs})
((cmdname, (arg1,arg2)), cmdname, (cmdname, (arg1,)))
outputs an ordered structure on the form
((cmdname, (args,), {kwargs}), ...), where the two last
parts of each tuple may be empty
Args: oobstruct (str or iterable): A structure representing
an oob command on one of the following forms:
- "cmdname"
- "cmdname", "cmdname"
- ("cmdname", arg)
- ("cmdname",(args))
- ("cmdname",{kwargs}
- ("cmdname", (args), {kwargs})
- (("cmdname", (args,), {kwargs}), ("cmdname", (args,), {kwargs}))
and any combination of argument-less commands or commands with only
args, only kwargs or both.
Returns:
structure (tuple): A generic OOB structure on the form
`((cmdname, (args,), {kwargs}), ...)`, where the two last
args and kwargs may be empty
"""
def _parse(oobstruct):
slen = len(oobstruct)
@ -217,17 +222,17 @@ class PortalSessionHandler(SessionHandler):
return (oobstruct[0], (), {})
elif slen == 2:
if isinstance(oobstruct[1], dict):
# cmdname, {kwargs}
# (cmdname, {kwargs})
return (oobstruct[0], (), dict(oobstruct[1]))
elif isinstance(oobstruct[1], (tuple, list)):
# cmdname, (args,)
return (oobstruct[0], list(oobstruct[1]), {})
# (cmdname, (args,))
return (oobstruct[0], tuple(oobstruct[1]), {})
else:
# cmdname, cmdname
return ((oobstruct[0], (), {}), (oobstruct[1].lower(), (), {}))
# (cmdname, arg)
return (oobstruct[0], (oobstruct[1],), {})
else:
# cmdname, (args,), {kwargs}
return (oobstruct[0], list(oobstruct[1]), dict(oobstruct[2]))
# (cmdname, (args,), {kwargs})
return (oobstruct[0], tuple(oobstruct[1]), dict(oobstruct[2]))
if hasattr(oobstruct, "__iter__"):
# differentiate between (cmdname, cmdname),
@ -264,7 +269,9 @@ class PortalSessionHandler(SessionHandler):
if session:
# convert oob to the generic format
if "oob" in kwargs:
print "oobstruct_parser in:", kwargs["oob"]
kwargs["oob"] = self.oobstruct_parser(kwargs["oob"])
print "oobstruct_parser out:", kwargs["oob"]
session.data_out(text=text, **kwargs)
PORTAL_SESSIONS = PortalSessionHandler()

View file

@ -26,8 +26,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
"""
def connectionMade(self):
"""
This is called when the connection is first
established.
This is called when the connection is first established.
"""
# initialize the session
self.iaw_mode = False
@ -35,7 +34,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
client_address = self.transport.client
# this number is counted down for every handshake that completes.
# when it reaches 0 the portal/server syncs their data
self.handshakes = 6 # naws, ttype, mccp, mssp, oob, mxp
self.handshakes = 7 # naws, ttype, mccp, mssp, msdp, gmcp, mxp
self.init_session("telnet", client_address, self.factory.sessionhandler)
# negotiate client size
@ -47,7 +46,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
self.mccp = Mccp(self)
# negotiate mssp (crawler communication)
self.mssp = mssp.Mssp(self)
# oob communication (MSDP, GMCP)
# oob communication (MSDP, GMCP) - two handshake calls!
self.oob = telnet_oob.TelnetOOB(self)
# mxp support
self.mxp = Mxp(self)
@ -67,7 +66,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
When all have reported, a sync with the server is performed.
The system will force-call this sync after a small time to handle
clients that don't reply to handshakes at all.
info - debug text from the protocol calling
"""
if self.handshakes > 0:
if force:
@ -239,7 +237,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
if "oob" in kwargs and "OOB" in self.protocol_flags:
# oob is a list of [(cmdname, arg, kwarg), ...]
for cmdname, args, kwargs in kwargs["oob"]:
print "telnet oob data_out:", cmdname, args, kwargs
#print "telnet oob data_out:", cmdname, args, kwargs
self.oob.data_out(cmdname, *args, **kwargs)
# parse **kwargs, falling back to ttype if nothing is given explicitly

View file

@ -33,7 +33,7 @@ MSDP_ARRAY_OPEN = chr(5)
MSDP_ARRAY_CLOSE = chr(6)
# GMCP
GMCP = chr(200)
GMCP = chr(201)
# General Telnet
IAC = chr(255)
@ -71,16 +71,18 @@ class TelnetOOB(object):
self.protocol.protocol_flags['OOB'] = False
self.MSDP = False
self.GMCP = False
# detect MSDP first
self.protocol.negotiationMap[MSDP] = self.data_in
# ask for the available protocols and assign decoders
# (note that handshake_done() will be called twice!)
self.protocol.negotiationMap[MSDP] = self.decode_msdp
self.protocol.negotiationMap[GMCP] = self.decode_gmcp
self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp)
self.protocol.will(GMCP).addCallbacks(self.do_gmcp, self.no_gmcp)
self.oob_reported = {}
def no_msdp(self, option):
"No msdp supported or wanted"
# no msdp, check GMCP
self.protocol.negotiationMap[GMCP] = self.data_in
self.protocol.will(GMCP).addCallbacks(self.do_oob, self.no_oob)
self.protocol.handshake_done()
def do_msdp(self, option):
"MSDP supported by client"
@ -122,16 +124,16 @@ class TelnetOOB(object):
if cmdname == "MSDP_ARRAY":
msdp_string = "".join(["%s%s" % (MSDP_VAL, val) for val in args])
else:
msdp_string = "%s%s%s" % (MSDP_VAR, cmdname.upper(), "".join(
msdp_string = "%s%s%s" % (MSDP_VAR, cmdname, "".join(
"%s%s" % (MSDP_VAL, val) for val in args))
elif kwargs:
if cmdname == "MSDP_TABLE":
msdp_string = "".join(["%s%s%s%s" % (MSDP_VAR, key, MSDP_VAL, val)
for key, val in kwargs.items()])
else:
msdp_string = "%s%s%s" % (MSDP_VAR. cmdname.upper(), "".join(
msdp_string = "%s%s%s" % (MSDP_VAR. cmdname, "".join(
["%s%s%s%s" % (MSDP_VAR, key, MSDP_VAL, val) for key, val in kwargs.items()]))
print "encode msdp result:", cmdname, args, kwargs, "->", msdp_string
#print "encode msdp result:", cmdname, args, kwargs, "->", msdp_string
return force_str(msdp_string)
def encode_gmcp(self, cmdname, *args, **kwargs):
@ -145,8 +147,8 @@ class TelnetOOB(object):
cmdname is generally recommended to be a string on the form
Module.Submodule.Function
"""
if cmdname in ("SEND", "ECHO", "REPORT", "UNREPORT", "LIST"):
# we wrap the standard MSDP commands in a MSDP. submodule
if cmdname in ("SEND", "REPORT", "UNREPORT", "LIST"):
# we wrap the standard MSDP commands in a MSDP.submodule
# here as far as GMCP is concerned.
cmdname = "MSDP.%s" % cmdname
elif cmdname in ("MSDP_ARRAY", "MSDP_TABLE"):
@ -156,15 +158,16 @@ class TelnetOOB(object):
gmcp_string = "%s %s" % (cmdname, json.dumps(args))
elif kwargs:
gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs))
print "gmcp_encode", cmdname, args, kwargs, "->", gmcp_string
return force_str(gmcp_string).strip()
def decode_msdp(self, data):
"""
Decodes incoming MSDP data
cmdname, var --> cmdname arg
cmdname, array --> cmdname *args
cmdname, table --> cmdname **kwargs
cmdname var --> cmdname arg
cmdname array --> cmdname *args
cmdname table --> cmdname **kwargs
"""
tables = {}
@ -174,8 +177,6 @@ class TelnetOOB(object):
if hasattr(data, "__iter__"):
data = "".join(data)
#logger.log_infomsg("MSDP SUBNEGOTIATION: %s" % data)
# decode
for key, table in msdp_regex_table.findall(data):
tables[key] = {}
@ -190,9 +191,9 @@ class TelnetOOB(object):
for varval in msdp_regex_var.split(msdp_regex_array.sub("", msdp_regex_table.sub("", data))):
# get remaining varvals after cleaning away tables/arrays
parts = msdp_regex_val.split(varval)
variables[parts[0].upper()] = tuple(parts[1:]) if len(parts) > 1 else ("", )
variables[parts[0]] = tuple(parts[1:]) if len(parts) > 1 else ("", )
print "OOB: MSDP decode:", data, "->", variables, arrays, tables
#print "OOB: MSDP decode:", data, "->", variables, arrays, tables
# send to the sessionhandler
if data:
@ -215,16 +216,27 @@ class TelnetOOB(object):
cmdname {key:arg, key:arg, ...} -> cmdname **kwargs
"""
if hasattr(data, "__iter__"):
data = "".join(data)
print "decode_gmcp:", data
if data:
splits = data.split(" ", 1)
cmdname = splits[0]
if len(splits) < 2:
self.protocol.data_in(oob=(cmdname, (), {}))
else:
struct = json.loads(splits[1])
self.protocol.data_in(oob=(cmdname,
struct if isinstance(struct, list) else (),
struct if isinstance(struct, dict) else {}))
elif splits[1]:
struct = json.loads(json.dumps(splits[1]))
args, kwargs = (), {}
if hasattr(struct, "__iter__"):
if isinstance(struct, dict):
kwargs = struct
else:
args = tuple(struct)
else:
args = (struct,)
print "gmcp decode:", data, "->", cmdname, args, kwargs
self.protocol.data_in(oob=(cmdname, args, kwargs))
# access methods
@ -232,20 +244,10 @@ class TelnetOOB(object):
"""
Return a msdp-valid subnegotiation across the protocol.
"""
#print "data_out:", encoded_oob
if self.MSDP:
encoded_oob = self.encode_msdp(cmdname, *args, **kwargs)
print "data_out:", encoded_oob
self.protocol._write(IAC + SB + MSDP + encoded_oob + IAC + SE)
else:
if self.GMCP:
encoded_oob = self.encode_gmcp(cmdname, *args, **kwargs)
self.protocol._write(IAC + SB + GMCP + encoded_oob + IAC + SE)
def data_in(self, data):
"""
Send oob data to Evennia. The self.decode_* methods send to
protocol.data_in() themselves.
"""
if self.MSDP:
self.decode_msdp(data)
else:
self.decode_gmcp(data)

View file

@ -438,6 +438,7 @@ class ServerSessionHandler(SessionHandler):
if not _OOB_HANDLER:
from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
funcname, args, kwargs = kwargs.pop("oob")
print "OOB session.data_in:", funcname, args, kwargs
if funcname:
_OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs)