Implemented GMCP with the new oobhandler. Resolves #208.
This commit is contained in:
parent
00aa28004c
commit
08525f11ee
8 changed files with 172 additions and 144 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"]))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue