Made GMCP/MSDP work for tintin++. Mudlet seems to send the handshake differently.
This commit is contained in:
parent
49c1254de7
commit
006e367330
6 changed files with 206 additions and 138 deletions
|
|
@ -53,7 +53,9 @@ def text(session, *args, **kwargs):
|
||||||
session.update_session_counters()
|
session.update_session_counters()
|
||||||
|
|
||||||
def echo(session, *args, **kwargs):
|
def echo(session, *args, **kwargs):
|
||||||
session.data_out(text=(args, kwargs))
|
print "Inputfunc echo:", session, args, kwargs
|
||||||
|
session.data_out(text="Echo returns: ")
|
||||||
|
session.data_out(echo=(args, kwargs))
|
||||||
|
|
||||||
def default(session, cmdname, *args, **kwargs):
|
def default(session, cmdname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -61,10 +63,10 @@ def default(session, cmdname, *args, **kwargs):
|
||||||
it will get `cmdname` as the first argument.
|
it will get `cmdname` as the first argument.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
err = "Input command not recognized:\n" \
|
err = "Session {sessid}: Input command not recognized:\n" \
|
||||||
" name: {cmdname}\n" \
|
" name: '{cmdname}'\n" \
|
||||||
" args, kwargs: {args}, {kwargs}"
|
" args, kwargs: {args}, {kwargs}"
|
||||||
log_err(err.format(cmdname=cmdname, args=args, kwargs=kwargs))
|
log_err(err.format(sessid=session.sessid, cmdname=cmdname, args=args, kwargs=kwargs))
|
||||||
|
|
||||||
#------------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for session in self.values():
|
for session in self.values():
|
||||||
self.data_out(session, text=message)
|
self.data_out(session, text=[[message],{}])
|
||||||
|
|
||||||
def data_in(self, session, **kwargs):
|
def data_in(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -334,9 +334,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.data_out(session, text=_ERROR_COMMAND_OVERFLOW)
|
self.data_out(session, text=_ERROR_COMMAND_OVERFLOW)
|
||||||
return
|
return
|
||||||
# scrub data
|
# scrub data
|
||||||
print ("portalsessionhandler before clean:", session, kwargs)
|
|
||||||
kwargs = self.clean_senddata(session, kwargs)
|
kwargs = self.clean_senddata(session, kwargs)
|
||||||
print ("portalsessionhandler after clean:", session, kwargs)
|
|
||||||
|
|
||||||
# relay data to Server
|
# relay data to Server
|
||||||
self.command_counter += 1
|
self.command_counter += 1
|
||||||
|
|
@ -370,7 +368,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
# distribute outgoing data to the correct session methods.
|
# distribute outgoing data to the correct session methods.
|
||||||
if session:
|
if session:
|
||||||
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
||||||
funcname = "send_%s" % cmdname
|
funcname = "send_%s" % cmdname.strip().lower()
|
||||||
if hasattr(session, funcname):
|
if hasattr(session, funcname):
|
||||||
# better to use hassattr here over try..except
|
# better to use hassattr here over try..except
|
||||||
# - avoids hiding AttributeErrors in the call.
|
# - avoids hiding AttributeErrors in the call.
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ from evennia.server.session import Session
|
||||||
from evennia.server.portal import ttype, mssp, telnet_oob, naws
|
from evennia.server.portal import ttype, mssp, telnet_oob, naws
|
||||||
from evennia.server.portal.mccp import Mccp, mccp_compress, MCCP
|
from evennia.server.portal.mccp import Mccp, mccp_compress, MCCP
|
||||||
from evennia.server.portal.mxp import Mxp, mxp_parse
|
from evennia.server.portal.mxp import Mxp, mxp_parse
|
||||||
from evennia.utils import utils, ansi, logger
|
from evennia.utils import ansi, logger
|
||||||
|
from evennia.utils.utils import to_str
|
||||||
|
|
||||||
IAC = chr(255)
|
IAC = chr(255)
|
||||||
NOP = chr(241)
|
NOP = chr(241)
|
||||||
|
|
@ -289,11 +290,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
Note that it must be actively turned back on again!
|
Note that it must be actively turned back on again!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
print "telnet.send_text", args,kwargs
|
#print "telnet.send_text", args,kwargs
|
||||||
if args:
|
text = args[0] if args else ""
|
||||||
text = args[0]
|
if text is None:
|
||||||
if text is None:
|
return
|
||||||
return
|
text = to_str(text, force_string=True)
|
||||||
|
|
||||||
# handle arguments
|
# handle arguments
|
||||||
options = kwargs.get("options", {})
|
options = kwargs.get("options", {})
|
||||||
|
|
@ -354,4 +355,5 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
Send other oob data
|
Send other oob data
|
||||||
"""
|
"""
|
||||||
if not cmdname == "options":
|
if not cmdname == "options":
|
||||||
print "telnet.send_default not implemented yet! ", args
|
print "telnet.send_default:", cmdname, args, kwargs
|
||||||
|
self.oob.data_out(cmdname, *args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,28 @@
|
||||||
|
|
||||||
Telnet OOB (Out of band communication)
|
Telnet OOB (Out of band communication)
|
||||||
|
|
||||||
This implements the following telnet oob protocols: MSDP (Mud Server
|
OOB protocols allow for asynchronous communication between Evennia and
|
||||||
Data Protocol) GMCP (Generic Mud Communication Protocol)
|
compliant telnet clients. The "text" type of send command will always
|
||||||
|
be sent "in-band", appearing in the client's main text output. OOB
|
||||||
|
commands, by contrast, can have many forms and it is up to the client
|
||||||
|
how and if they are handled. Examples of OOB instructions could be to
|
||||||
|
instruct the client to play sounds or to update a graphical health
|
||||||
|
bar.
|
||||||
|
|
||||||
This implements the MSDP protocol as per
|
> Note that in Evennia's Web client, all send commands are "OOB
|
||||||
http://tintin.sourceforge.net/msdp/ and the GMCP protocol as per
|
commands", (including the "text" one), there is no equivalence to
|
||||||
http://www.ironrealms.com/rapture/manual/files/FeatGMCP-txt.html#Generic_MUD_Communication_Protocol%28GMCP%29
|
MSDP/GMCP for the webclient since it doesn't need it.
|
||||||
|
|
||||||
|
This implements the following telnet OOB communication protocols:
|
||||||
|
- MSDP (Mud Server Data Protocol), as per
|
||||||
|
http://tintin.sourceforge.net/msdp/
|
||||||
|
- GMCP (Generic Mud Communication Protocol) as per
|
||||||
|
http://www.ironrealms.com/rapture/manual/files/FeatGMCP-txt.html#Generic_MUD_Communication_Protocol%28GMCP%29
|
||||||
|
|
||||||
Following the lead of KaVir's protocol snippet, we first check if
|
Following the lead of KaVir's protocol snippet, we first check if
|
||||||
client supports MSDP and if not, we fallback to GMCP with a MSDP
|
client supports MSDP and if not, we fallback to GMCP with a MSDP
|
||||||
header where applicable.
|
header where applicable.
|
||||||
|
|
||||||
OOB manages out-of-band communication between the client and server,
|
|
||||||
for updating health bars etc. See also GMCP which is another standard
|
|
||||||
doing the same thing.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from builtins import object
|
from builtins import object
|
||||||
import re
|
import re
|
||||||
|
|
@ -25,12 +32,12 @@ from evennia.utils.utils import to_str
|
||||||
|
|
||||||
# MSDP-relevant telnet cmd/opt-codes
|
# MSDP-relevant telnet cmd/opt-codes
|
||||||
MSDP = chr(69)
|
MSDP = chr(69)
|
||||||
MSDP_VAR = chr(1)
|
MSDP_VAR = chr(1) #^A
|
||||||
MSDP_VAL = chr(2)
|
MSDP_VAL = chr(2) #^B
|
||||||
MSDP_TABLE_OPEN = chr(3)
|
MSDP_TABLE_OPEN = chr(3) #^C
|
||||||
MSDP_TABLE_CLOSE = chr(4)
|
MSDP_TABLE_CLOSE = chr(4) #^D
|
||||||
MSDP_ARRAY_OPEN = chr(5)
|
MSDP_ARRAY_OPEN = chr(5) #^E
|
||||||
MSDP_ARRAY_CLOSE = chr(6)
|
MSDP_ARRAY_CLOSE = chr(6) #^F
|
||||||
|
|
||||||
# GMCP
|
# GMCP
|
||||||
GMCP = chr(201)
|
GMCP = chr(201)
|
||||||
|
|
@ -44,15 +51,16 @@ force_str = lambda inp: to_str(inp, force_string=True)
|
||||||
|
|
||||||
# pre-compiled regexes
|
# pre-compiled regexes
|
||||||
# returns 2-tuple
|
# returns 2-tuple
|
||||||
msdp_regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
msdp_regex_table = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
||||||
MSDP_ARRAY_OPEN,
|
|
||||||
MSDP_ARRAY_CLOSE))
|
|
||||||
# returns 2-tuple (may be nested)
|
|
||||||
msdp_regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
|
||||||
MSDP_TABLE_OPEN,
|
MSDP_TABLE_OPEN,
|
||||||
MSDP_TABLE_CLOSE))
|
MSDP_TABLE_CLOSE))
|
||||||
msdp_regex_var = re.compile(MSDP_VAR)
|
# returns 2-tuple
|
||||||
msdp_regex_val = re.compile(MSDP_VAL)
|
msdp_regex_array = re.compile(r"%s\s*(\w*?)\s*%s\s*%s(.*?)%s" % (MSDP_VAR, MSDP_VAL,
|
||||||
|
MSDP_ARRAY_OPEN,
|
||||||
|
MSDP_ARRAY_CLOSE))
|
||||||
|
msdp_regex_var = re.compile(r"%s" % MSDP_VAR)
|
||||||
|
msdp_regex_val = re.compile(r"%s" % MSDP_VAL)
|
||||||
|
|
||||||
|
|
||||||
# Msdp object handler
|
# Msdp object handler
|
||||||
|
|
||||||
|
|
@ -104,6 +112,7 @@ class TelnetOOB(object):
|
||||||
self.MSDP = True
|
self.MSDP = True
|
||||||
self.protocol.protocol_flags['OOB'] = True
|
self.protocol.protocol_flags['OOB'] = True
|
||||||
self.protocol.handshake_done()
|
self.protocol.handshake_done()
|
||||||
|
print "Activated MSDP"
|
||||||
|
|
||||||
def no_gmcp(self, option):
|
def no_gmcp(self, option):
|
||||||
"""
|
"""
|
||||||
|
|
@ -127,16 +136,16 @@ class TelnetOOB(object):
|
||||||
self.GMCP = True
|
self.GMCP = True
|
||||||
self.protocol.protocol_flags['OOB'] = True
|
self.protocol.protocol_flags['OOB'] = True
|
||||||
self.protocol.handshake_done()
|
self.protocol.handshake_done()
|
||||||
|
print "Activated GMCP"
|
||||||
|
|
||||||
# encoders
|
# encoders
|
||||||
|
|
||||||
def encode_msdp(self, cmdname, *args, **kwargs):
|
def encode_msdp(self, cmdname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
handle return data from cmdname by converting it to a proper
|
Encode into a valid MSDP command.
|
||||||
msdp structure. These are the combinations we support:
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmdname (str): Name of OOB command.
|
cmdname (str): Name of send instruction.
|
||||||
args, kwargs (any): Arguments to OOB command.
|
args, kwargs (any): Arguments to OOB command.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
@ -146,55 +155,72 @@ class TelnetOOB(object):
|
||||||
MSDP_ARRAY *args -> MSDP_ARRAY
|
MSDP_ARRAY *args -> MSDP_ARRAY
|
||||||
MSDP_TABLE **kwargs -> MSDP_TABLE
|
MSDP_TABLE **kwargs -> MSDP_TABLE
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The output of this encoding will always be an
|
||||||
|
MSDP structure on the form
|
||||||
|
|
||||||
|
```
|
||||||
|
MSDP_VAR cmdname
|
||||||
|
MSDP_VAL MSDP_ARRAY_OPEN
|
||||||
|
MSDP_VAL MSDP_ARRAY_OPEN
|
||||||
|
MSDP_VAL arg1
|
||||||
|
MSDP_VAL arg2
|
||||||
|
...
|
||||||
|
MSDP_ARRAY_CLOSE
|
||||||
|
MSDP_VAL MSDP_TABLE_OPEN
|
||||||
|
MSDP_VAR "key1" MSDP_VAL "val1"
|
||||||
|
MSDP_VAR "key2" MSDP_VAL "val2"
|
||||||
|
...
|
||||||
|
MSDP_TABLE_CLOSE
|
||||||
|
MSDP_ARRAY_CLOSE
|
||||||
|
```
|
||||||
|
|
||||||
|
That is, it's a sequence "cmdnmame [[args] {kwargs}]"
|
||||||
|
|
||||||
|
Further nesting is not supported, so if an argument consists
|
||||||
|
of an array (for example), that array will be json-converted
|
||||||
|
to a string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
msdp_string = ""
|
msdp_msg = "{msdp_var}{msdp_cmdname}" \
|
||||||
if args:
|
"{msdp_val}{msdp_array_open}" \
|
||||||
if cmdname == "MSDP_ARRAY":
|
"{msdp_val}{msdp_array_open}" \
|
||||||
msdp_string = "".join(["%s%s" % (MSDP_VAL, val) for val in args])
|
"{msdp_args}" \
|
||||||
else:
|
"{msdp_array_close}" \
|
||||||
msdp_string = "%s%s%s" % (MSDP_VAR, cmdname, "".join(
|
"{msdp_val}{msdp_table_open}" \
|
||||||
"%s%s" % (MSDP_VAL, val) for val in args))
|
"{msdp_kwargs}" \
|
||||||
elif kwargs:
|
"{msdp_table_close}" \
|
||||||
if cmdname == "MSDP_TABLE":
|
"{msdp_array_close}".format(
|
||||||
msdp_string = "".join(["%s%s%s%s" % (MSDP_VAR, key, MSDP_VAL, val)
|
msdp_var=MSDP_VAR, msdp_val=MSDP_VAL,
|
||||||
for key, val in kwargs.items()])
|
msdp_array_open=MSDP_ARRAY_OPEN,
|
||||||
else:
|
msdp_array_close=MSDP_ARRAY_CLOSE,
|
||||||
msdp_string = "%s%s%s" % (MSDP_VAR. cmdname, "".join(
|
msdp_table_open=MSDP_TABLE_OPEN,
|
||||||
["%s%s%s%s" % (MSDP_VAR, key, MSDP_VAL, val) for key, val in kwargs.items()]))
|
msdp_table_close=MSDP_TABLE_CLOSE,
|
||||||
return force_str(msdp_string)
|
msdp_cmdname = json.dumps(cmdname),
|
||||||
|
msdp_args = "".join("%s%s" % (MSDP_VAL, json.dumps(val)) for val in args),
|
||||||
|
msdp_kwargs = "".join("%s%s%s%s" % (MSDP_VAR, key, MSDP_VAL, json.dumps(val))
|
||||||
|
for key, val in kwargs.iteritems()))
|
||||||
|
return msdp_msg
|
||||||
|
|
||||||
def encode_gmcp(self, cmdname, *args, **kwargs):
|
def encode_gmcp(self, cmdname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Encode GMCP messages.
|
Encode into GMCP messages.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmdname (str): GMCP OOB command name.
|
cmdname (str): GMCP OOB command name.
|
||||||
args, kwargs (any): Arguments to OOB command.
|
args, kwargs (any): Arguments to OOB command.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
Gmcp messages are on one of the following outgoing forms:
|
GMCP messages will be outgoing on the following
|
||||||
|
form (the non-JSON cmdname at the start is what
|
||||||
|
IRE games use, supposedly, and what clients appear
|
||||||
|
to have adopted):
|
||||||
|
|
||||||
- cmdname string -> cmdname string
|
cmdname [[args], {kwargs}]
|
||||||
- cmdname *args -> cmdname [arg, arg, arg, ...]
|
|
||||||
- cmdname **kwargs -> cmdname {key:arg, key:arg, ...}
|
|
||||||
|
|
||||||
cmdname is generally recommended to be a string on the form
|
|
||||||
Module.Submodule.Function
|
|
||||||
"""
|
"""
|
||||||
if cmdname in ("SEND", "REPORT", "UNREPORT", "LIST"):
|
print "GMCP out:", json.dumps([args, kwargs])
|
||||||
# we wrap the standard MSDP commands in a MSDP.submodule
|
return json.dumps("%s %s" % (cmdname, json.dumps([args, kwargs])))
|
||||||
# here as far as GMCP is concerned.
|
|
||||||
cmdname = "MSDP.%s" % cmdname
|
|
||||||
elif cmdname in ("MSDP_ARRAY", "MSDP_TABLE"):
|
|
||||||
# no cmdname should accompany these, just the MSDP wrapper
|
|
||||||
cmdname = "MSDP"
|
|
||||||
|
|
||||||
gmcp_string = ""
|
|
||||||
if args:
|
|
||||||
gmcp_string = "%s %s" % (cmdname, json.dumps(args))
|
|
||||||
elif kwargs:
|
|
||||||
gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs))
|
|
||||||
return force_str(gmcp_string).strip()
|
|
||||||
|
|
||||||
def decode_msdp(self, data):
|
def decode_msdp(self, data):
|
||||||
"""
|
"""
|
||||||
|
|
@ -204,45 +230,79 @@ class TelnetOOB(object):
|
||||||
data (str or list): MSDP data.
|
data (str or list): MSDP data.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
cmdname var --> cmdname arg
|
Clients should always send MSDP data on
|
||||||
cmdname array --> cmdname *args
|
one of the following forms:
|
||||||
cmdname table --> cmdname **kwargs
|
|
||||||
|
cmdname -> [cmdname, [], {}]
|
||||||
|
cmdname val -> [cmdname, [val], {}]
|
||||||
|
cmdname array -> [cmdname, [array], {}]
|
||||||
|
cmdname table -> [cmdname, [], {table}]
|
||||||
|
cmdname array cmdname table -> [cmdname, [array], {table}]
|
||||||
|
|
||||||
|
Observe that all MSDP_VARS are used to identify cmdnames,
|
||||||
|
so if there are multiple arrays with the same cmdname
|
||||||
|
given, they will be merged into one argument array, same
|
||||||
|
for tables. Different MSDP_VARS (outside tables) will be
|
||||||
|
identified as separate cmdnames.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if hasattr(data, "__iter__"):
|
||||||
|
data = "".join(data)
|
||||||
|
|
||||||
tables = {}
|
tables = {}
|
||||||
arrays = {}
|
arrays = {}
|
||||||
variables = {}
|
variables = {}
|
||||||
|
|
||||||
if hasattr(data, "__iter__"):
|
# decode tables
|
||||||
data = "".join(data)
|
|
||||||
|
|
||||||
# decode
|
|
||||||
for key, table in msdp_regex_table.findall(data):
|
for key, table in msdp_regex_table.findall(data):
|
||||||
tables[key] = {}
|
tables[key] = {} if not key in tables else tables[key]
|
||||||
for varval in msdp_regex_var.split(table):
|
for varval in msdp_regex_var.split(table)[1:]:
|
||||||
parts = msdp_regex_val.split(varval)
|
var, val = msdp_regex_val.split(varval, 1)
|
||||||
tables[key].expand({parts[0]: tuple(parts[1:]) if len(parts) > 1 else ("",)})
|
tables[key][var] = val
|
||||||
for key, array in msdp_regex_array.findall(data):
|
|
||||||
arrays[key] = []
|
# decode arrays from all that was not a table
|
||||||
for val in msdp_regex_val.split(array):
|
data_no_tables = msdp_regex_table.sub("", data)
|
||||||
arrays[key].append(val)
|
for key, array in msdp_regex_array.findall(data_no_tables):
|
||||||
arrays[key] = tuple(arrays[key])
|
arrays[key] = [] if not key in arrays else arrays[key]
|
||||||
for varval in msdp_regex_var.split(msdp_regex_array.sub("", msdp_regex_table.sub("", data))):
|
parts = msdp_regex_val.split(array)
|
||||||
# get remaining varvals after cleaning away tables/arrays
|
if len(parts) == 2:
|
||||||
parts = msdp_regex_val.split(varval)
|
arrays[key].append(parts[1])
|
||||||
variables[parts[0]] = tuple(parts[1:]) if len(parts) > 1 else ("", )
|
elif len(parts) > 1:
|
||||||
|
arrays[key].extend(parts[1:])
|
||||||
|
|
||||||
|
# decode remainders from all that were not tables or arrays
|
||||||
|
data_no_tables_or_arrays = msdp_regex_array.sub("", data_no_tables)
|
||||||
|
for varval in msdp_regex_var.split(data_no_tables_or_arrays):
|
||||||
|
# get remaining varvals after cleaning away tables/arrays. If mathcing
|
||||||
|
# an existing key in arrays, it will be added as an argument to that command,
|
||||||
|
# otherwise it will be treated as a command without argument.
|
||||||
|
parts = msdp_regex_val.split(varval)
|
||||||
|
if len(parts) == 2:
|
||||||
|
variables[parts[0]] = parts[1]
|
||||||
|
elif len(parts) > 1:
|
||||||
|
variables[parts[0]] = parts[1:]
|
||||||
|
|
||||||
|
cmds = {}
|
||||||
|
# merge matching table/array/variables together
|
||||||
|
for key, table in tables.iteritems():
|
||||||
|
args, kwargs = [], table
|
||||||
|
if key in arrays:
|
||||||
|
args.extend(arrays.pop(key))
|
||||||
|
if key in variables:
|
||||||
|
args.append(variables.pop(key))
|
||||||
|
cmds[key] = [args, kwargs]
|
||||||
|
|
||||||
|
for key, arr in arrays.iteritems():
|
||||||
|
args, kwargs = arr, {}
|
||||||
|
if key in variables:
|
||||||
|
args.append(variables.pop(key))
|
||||||
|
cmds[key] = [args, kwargs]
|
||||||
|
|
||||||
|
for key, var in variables.iteritems():
|
||||||
|
cmds[key] = [[var], {}]
|
||||||
|
|
||||||
|
self.protocol.data_in(**cmds)
|
||||||
|
|
||||||
# send to the sessionhandler
|
|
||||||
if data:
|
|
||||||
for varname, var in variables.items():
|
|
||||||
# a simple function + argument
|
|
||||||
self.protocol.data_in(oob=(varname, var, {}))
|
|
||||||
for arrayname, array in arrays.items():
|
|
||||||
# we assume the array are multiple arguments to the function
|
|
||||||
self.protocol.data_in(oob=(arrayname, array, {}))
|
|
||||||
for tablename, table in tables.items():
|
|
||||||
# we assume tables are keyword arguments to the function
|
|
||||||
self.protocol.data_in(oob=(tablename, (), table))
|
|
||||||
|
|
||||||
def decode_gmcp(self, data):
|
def decode_gmcp(self, data):
|
||||||
"""
|
"""
|
||||||
|
|
@ -252,39 +312,48 @@ class TelnetOOB(object):
|
||||||
data (str or list): GMCP data.
|
data (str or list): GMCP data.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
cmdname string -> cmdname arg
|
Clients tend to send data on the form "cmdname <structure>".
|
||||||
cmdname [arg, arg,...] -> cmdname *args
|
We assume the structure is valid JSON.
|
||||||
cmdname {key:arg, key:arg, ...} -> cmdname **kwargs
|
|
||||||
|
The following is parsed into Evennia's formal structure:
|
||||||
|
|
||||||
|
cmdname -> [cmdname, [], {}]
|
||||||
|
cmdname string -> [cmdname, [string], {}]
|
||||||
|
cmdname [arg, arg,...] -> [cmdname, [args], {}]
|
||||||
|
cmdname {key:arg, key:arg, ...} -> [cmdname, [], {kwargs}]
|
||||||
|
cmdname [[args], {kwargs}] -> [cmdname, [args], {kwargs}]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if hasattr(data, "__iter__"):
|
if hasattr(data, "__iter__"):
|
||||||
data = "".join(data)
|
data = "".join(data)
|
||||||
|
|
||||||
|
print "decode_gmcp in:", data
|
||||||
if data:
|
if data:
|
||||||
splits = data.split(None, 1)
|
try:
|
||||||
cmdname = splits[0]
|
cmdname, structure = data.split(None, 1)
|
||||||
if len(splits) < 2:
|
except ValueError:
|
||||||
self.protocol.data_in(oob=(cmdname, (), {}))
|
self.protocol.data_in(**{data: [[],{}]})
|
||||||
elif splits[1]:
|
return
|
||||||
try:
|
try:
|
||||||
struct = json.loads(splits[1])
|
structure = json.loads(structure)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
struct = splits[1]
|
pass
|
||||||
args, kwargs = (), {}
|
args, kwargs = [], {}
|
||||||
if hasattr(struct, "__iter__"):
|
if hasattr(structure, "__iter__"):
|
||||||
if isinstance(struct, dict):
|
if isinstance(structure, dict):
|
||||||
kwargs = struct
|
kwargs = structure
|
||||||
else:
|
|
||||||
args = tuple(struct)
|
|
||||||
else:
|
else:
|
||||||
args = (struct,)
|
args = list(structure)
|
||||||
self.protocol.data_in(oob=(cmdname, args, kwargs))
|
else:
|
||||||
|
args = (structure,)
|
||||||
|
print "gmcp data in:", {cmdname: [args, kwargs]}
|
||||||
|
self.protocol.data_in(**{cmdname: [args, kwargs]})
|
||||||
|
|
||||||
# access methods
|
# access methods
|
||||||
|
|
||||||
def data_out(self, cmdname, *args, **kwargs):
|
def data_out(self, cmdname, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return a msdp-valid subnegotiation across the protocol.
|
Return a MSDP- or GMCP-valid subnegotiation across the protocol.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmdname (str): OOB-command name.
|
cmdname (str): OOB-command name.
|
||||||
|
|
@ -293,7 +362,9 @@ class TelnetOOB(object):
|
||||||
"""
|
"""
|
||||||
if self.MSDP:
|
if self.MSDP:
|
||||||
encoded_oob = self.encode_msdp(cmdname, *args, **kwargs)
|
encoded_oob = self.encode_msdp(cmdname, *args, **kwargs)
|
||||||
|
print "sending MSDP:", encoded_oob
|
||||||
self.protocol._write(IAC + SB + MSDP + encoded_oob + IAC + SE)
|
self.protocol._write(IAC + SB + MSDP + encoded_oob + IAC + SE)
|
||||||
if self.GMCP:
|
if self.GMCP:
|
||||||
encoded_oob = self.encode_gmcp(cmdname, *args, **kwargs)
|
encoded_oob = self.encode_gmcp(cmdname, *args, **kwargs)
|
||||||
|
print "sending GMCP:", encoded_oob
|
||||||
self.protocol._write(IAC + SB + GMCP + encoded_oob + IAC + SE)
|
self.protocol._write(IAC + SB + GMCP + encoded_oob + IAC + SE)
|
||||||
|
|
|
||||||
|
|
@ -341,8 +341,6 @@ class ServerSession(Session):
|
||||||
for the protocol(s).
|
for the protocol(s).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
print "serversession.data_out:", kwargs
|
|
||||||
|
|
||||||
self.sessionhandler.data_out(self, **kwargs)
|
self.sessionhandler.data_out(self, **kwargs)
|
||||||
|
|
||||||
def msg(self, text=None, **kwargs):
|
def msg(self, text=None, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ _MODEL_MAP = None
|
||||||
|
|
||||||
_INPUT_FUNCS = {}
|
_INPUT_FUNCS = {}
|
||||||
for modname in make_iter(settings.INPUT_FUNC_MODULES):
|
for modname in make_iter(settings.INPUT_FUNC_MODULES):
|
||||||
print modname
|
|
||||||
_INPUT_FUNCS.update(callables_from_module(modname))
|
_INPUT_FUNCS.update(callables_from_module(modname))
|
||||||
|
|
||||||
def delayed_import():
|
def delayed_import():
|
||||||
|
|
@ -187,7 +186,6 @@ class SessionHandler(dict):
|
||||||
|
|
||||||
rkwargs = {}
|
rkwargs = {}
|
||||||
for key, data in kwargs.iteritems():
|
for key, data in kwargs.iteritems():
|
||||||
print "sessionhandler.clean_senddata:", key, data
|
|
||||||
key = _validate(key)
|
key = _validate(key)
|
||||||
if not data:
|
if not data:
|
||||||
rkwargs[key] = [ [], {} ]
|
rkwargs[key] = [ [], {} ]
|
||||||
|
|
@ -604,11 +602,9 @@ class ServerSessionHandler(SessionHandler):
|
||||||
the wire here.
|
the wire here.
|
||||||
"""
|
"""
|
||||||
# clean output for sending
|
# clean output for sending
|
||||||
print "sessionhandler before clean_senddata:", kwargs
|
|
||||||
kwargs = self.clean_senddata(session, kwargs)
|
kwargs = self.clean_senddata(session, kwargs)
|
||||||
|
|
||||||
# send across AMP
|
# send across AMP
|
||||||
print "sessionhandler after clean_senddata:", kwargs
|
|
||||||
self.server.amp_protocol.send_MsgServer2Portal(session,
|
self.server.amp_protocol.send_MsgServer2Portal(session,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
|
@ -628,12 +624,13 @@ class ServerSessionHandler(SessionHandler):
|
||||||
# distribute incoming data to the correct receiving methods.
|
# distribute incoming data to the correct receiving methods.
|
||||||
if session:
|
if session:
|
||||||
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
for cmdname, (cmdargs, cmdkwargs) in kwargs.iteritems():
|
||||||
|
cname = cmdname.strip().lower()
|
||||||
|
print "sessionhandler.data_in:", session, kwargs
|
||||||
try:
|
try:
|
||||||
if cmdname in _INPUT_FUNCS:
|
if cname in _INPUT_FUNCS:
|
||||||
print "sessionhandler: data_in", cmdname, cmdargs, cmdkwargs
|
_INPUT_FUNCS[cname](session, *cmdargs, **cmdkwargs)
|
||||||
_INPUT_FUNCS[cmdname](session, *cmdargs, **cmdkwargs)
|
|
||||||
else:
|
else:
|
||||||
_INPUT_FUNCS["default"](session, cmdname, *cmdargs, **cmdkwargs)
|
_INPUT_FUNCS["default"](session, cname, *cmdargs, **cmdkwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
log_trace()
|
log_trace()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue