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
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue