OOB passing data client->server. A few more default commands are needed.

This commit is contained in:
Griatch 2013-10-07 19:57:01 +02:00
parent 96c6ad4aff
commit 16bbe009c3
6 changed files with 103 additions and 48 deletions

View file

@ -26,10 +26,10 @@ from django.conf import settings
from src.server.models import ServerConfig
from src.server.sessionhandler import SESSIONS
from src.scripts.scripts import Script
from src.create import create_script
from src.utils.create import create_script
from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj
from src.utils import logger
from src.utils.utils import variable_from_module, to_str
from src.utils.utils import variable_from_module, to_str, is_iter, make_iter
_SA = object.__setattr__
_GA = object.__getattribute__
@ -307,7 +307,7 @@ class OOBHandler(object):
oob_tracker_name = "_track_db_value_change"
self.track(attrobj, tracker_key, attr_name, sessid, property_name=oob_tracker_name)
def run(self, func_key, *args, **kwargs):
def execute_cmd(self, func_key, *args, **kwargs):
"""
Retrieve oobfunc from OOB_FUNCS and execute it immediately
using *args and **kwargs

View file

@ -132,7 +132,7 @@ class Msdp(object):
"""
self.protocol = protocol
self.protocol.protocol_flags['MSDP'] = False
self.protocol.negotiationMap[MSDP] = self.msdp_to_func
self.protocol.negotiationMap[MSDP] = self.msdp_to_evennia
self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp)
self.msdp_reported = {}
@ -148,10 +148,7 @@ class Msdp(object):
print "msdp supported"
self.protocol.protocol_flags['MSDP'] = True
def parse_msdp(self, args):
"Called with arguments to subnegotiation"
def func_to_msdp(self, cmdname, data):
def evennia_to_msdp(self, cmdname, data):
"""
handle return data from cmdname by converting it to
a proper msdp structure. data can either be a single value (will be
@ -179,6 +176,7 @@ class Msdp(object):
def make_array(name, datalist, string):
"build a simple array. Arrays may not nest tables by definition."
print "make_array", datalist, string
string += MSDP_VAR + name + MSDP_ARRAY_OPEN
for val in datalist:
string += MSDP_VAL + val
@ -190,11 +188,11 @@ class Msdp(object):
elif hasattr(data, '__iter__'):
msdp_string = make_array(cmdname, data, "")
else:
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data if data!=None else ""
return msdp_string
def msdp_to_func(self, data):
def msdp_to_evennia(self, data):
"""
Handle a client's requested negotiation, converting
it into a function mapping - either one of the MSDP
@ -229,57 +227,55 @@ class Msdp(object):
print "MSDP: table, array, variables:", tables, arrays, variables
# all variables sent through msdp to Evennia are considered commands with arguments.
# there are three forms of commands possible through msdp:
#
# VARNAME VAR -> varname(var)
# ARRAYNAME VAR VAL VAR VAL VAR VAL ENDARRAY -> arrayname(val,val,val)
# TABLENAME TABLE VARNAME VAL VARNAME VAL ENDTABLE -> tablename(varname=val, varname=val)
#
ret = ""
# default MSDP functions
if "LIST" in variables:
ret += self.func_to_msdp("LIST", self.msdp_cmd_list(variables["LIST"]))
del variables["LIST"]
ret += self.evennia_to_msdp("LIST", self.msdp_cmd_list(*(variables.pop("LIST"),)))
if "REPORT" in variables:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*(variables["REPORT"],)))
del variables["REPORT"]
ret += self.evennia_to_msdp("REPORT", self.msdp_cmd_report(*(variables.pop("REPORT"),)))
if "REPORT" in arrays:
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*arrays["REPORT"]))
del arrays["REPORT"]
ret += self.evennia_to_msdp("REPORT", self.msdp_cmd_report(*arrays.pop("REPORT")))
if "RESET" in variables:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*(variables["RESET"],)))
del variables["RESET"]
ret += self.evennia_to_msdp("RESET", self.msdp_cmd_reset(*(variables.pop("RESET"),)))
if "RESET" in arrays:
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"]))
del arrays["RESET"]
ret += self.evennia_to_msdp("RESET", self.msdp_cmd_reset(*arrays.pop("RESET",)))
if "SEND" in variables:
ret += self.func_to_msdp("SEND", self.msdp_cmd_send(*(variables["SEND"],)))
del variables["SEND"]
ret += self.evennia_to_msdp("SEND", self.msdp_cmd_send(*(variables.pop("SEND",))))
if "SEND" in arrays:
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"]))
del arrays["SEND"]
ret += self.evennia_to_msdp("SEND",self.msdp_cmd_send(*arrays.pop("SEND")))
# if there are anything left we look for a custom function
for varname, var in variables.items():
# a simple function + argument
ooc_func = MSDP_COMMANDS_CUSTOM.get(varname.upper())
if ooc_func:
ret += self.func_to_msdp(varname, ooc_func(var))
ret += self.evennia_to_msdp(varname, ooc_func(var))
for arrayname, array in arrays.items():
# we assume the array are multiple arguments to the function
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
if ooc_func:
ret += self.func_to_msdp(arrayname, ooc_func(*array))
ret += self.evennia_to_msdp(arrayname, ooc_func(*array))
for tablename, table in tables.items():
# we assume tables are keyword arguments to the function
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
if ooc_func:
ret += self.func_to_msdp(tablename, ooc_func(**table))
ret += self.evennia_to_msdp(tablename, ooc_func(**table))
# return any result
if ret:
# send return value if it exists
self.msdp_send(ret)
ret = IAC + SB + MSDP + ret + IAC + SE
#ret = IAC + SB + MSDP + MSDP_VAR + "SEND" + MSDP_VAL + "Testsend" + IAC + SE
self.protocol._write(ret)
logger.log_infomsg("MSDP_RESULT: %s" % ret)
self.data_out(ret)
def msdp_send(self, msdp_string):
def data_out(self, msdp_string):
"""
Return a msdp-valid subnegotiation across the protocol.
"""
@ -295,21 +291,21 @@ class Msdp(object):
The List command allows for retrieving various info about the server/client
"""
if arg == 'COMMANDS':
return self.func_to_msdp(arg, MSDP_COMMANDS)
return self.evennia_to_msdp(arg, MSDP_COMMANDS)
elif arg == 'LISTS':
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
return self.evennia_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
"REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
elif arg == 'CONFIGURABLE_VARIABLES':
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
return self.evennia_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
elif arg == 'REPORTABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys())
return self.evennia_to_msdp(arg, MSDP_REPORTABLE.keys())
elif arg == 'REPORTED_VARIABLES':
# the dynamically set items to report
return self.func_to_msdp(arg, self.msdp_reported.keys())
return self.evennia_to_msdp(arg, self.msdp_reported.keys())
elif arg == 'SENDABLE_VARIABLES':
return self.func_to_msdp(arg, MSDP_SENDABLE.keys())
return self.evennia_to_msdp(arg, MSDP_SENDABLE.keys())
else:
return self.func_to_msdp("LIST", arg)
return self.evennia_to_msdp("LIST", arg)
# default msdp commands

View file

@ -138,6 +138,7 @@ class PortalSessionHandler(SessionHandler):
for session in self.sessions.values():
session.data_out(message)
def data_out(self, sessid, text=None, **kwargs):
"""
Called by server for having the portal relay messages and data

View file

@ -13,6 +13,7 @@ from src.server.session import Session
from src.server.portal import ttype, mssp, msdp
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
from src.utils import utils, ansi, logger
from src.utils.utils import make_iter, is_iter
_RE_N = re.compile(r"\{n$")
@ -36,14 +37,13 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# negotiate ttype (client info)
#self.ttype = ttype.Ttype(self)
# negotiate mssp (crawler communication)
self.mssp = mssp.Mssp(self)
#self.mssp = mssp.Mssp(self)
# msdp
#self.msdp = msdp.Msdp(self)
self.msdp = msdp.Msdp(self)
# add this new connection to sessionhandler so
# the Server becomes aware of it.
self.sessionhandler.connect(self)
def enableRemote(self, option):
"""
This sets up the remote-activated options we allow for this protocol.
@ -69,7 +69,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
else:
return super(TelnetProtocol, self).disableLocal(option)
def connectionLost(self, reason):
"""
This is executed when the connection is lost for
@ -163,6 +162,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
except Exception, e:
self.sendLine(str(e))
return
if "oob" in kwargs:
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob"))
if "MSDP" in self.protocol_flags:
print "oobstruct:", oobstruct
for cmdname, args in oobstruct:
print "cmdname, args:", cmdname, args
msdp_string = self.msdp.func_to_msdp(cmdname, args)
print "msdp_string:", msdp_string
self.msdp.data_out(msdp_string)
ttype = self.protocol_flags.get('TTYPE', {})
raw = kwargs.get("raw", False)
nomarkup = not (ttype or ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done"))

View file

@ -20,6 +20,7 @@ from src.server.session import Session
IDLE_COMMAND = settings.IDLE_COMMAND
_GA = object.__getattribute__
_ObjectDB = None
_OOB_HANDLER = None
# load optional out-of-band function module
OOB_PLUGIN_MODULE = settings.OOB_PLUGIN_MODULE
@ -178,8 +179,13 @@ class ServerSession(Session):
cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid)
self.update_session_counters()
if "oob" in kwargs:
# relay to OOB handler
pass
# handle oob instructions
global _OOB_HANDLER
if not _OOB_HANDLER:
from src.servever.oobhandler import OOB_HANDLER as _OOB_HANDLER
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None))
for (funcname, args, kwargs) in oobstruct:
_OOBHANDLER.execute_cmd(funcname, *args, **kwargs)
execute_cmd = data_in # alias

View file

@ -27,6 +27,7 @@ _ServerSession = None
_ServerConfig = None
_ScriptDB = None
# AMP signals
PCONN = chr(1) # portal session connect
PDISCONN = chr(2) # portal session disconnect
@ -357,6 +358,49 @@ class ServerSessionHandler(SessionHandler):
return self.sessions.get(sessid)
return None
def oobstruct_parser(self, oobstruct):
"""
Helper method for each session to use to parse oob structures
(The 'oob' kwarg of the msg() method)
allowed 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
"""
slen = len(oobstruct)
if not oobstruct:
return tuple(None, (), {})
elif not hasattr(oobstruct, "__iter__"):
# a singular command name, without arguments or kwargs
return (oobstruct.lower(), (), {})
# regardless of number of args/kwargs, the first element must be the function name.
# we will not catch this error if not, but allow it to propagate.
if slen == 1:
return (oobstruct[0].lower(), (), {})
elif slen == 2:
if isinstance(oobstruct[1], dict):
# cmdname, {kwargs}
return (oobstruct[0].lower(), (), dict((key.lower(), val) for key,val in oobstruct[1].items()))
elif isinstance(oobstruct[1], (tuple, list)):
# cmdname, (args,)
return (oobstruct[0].lower(), tuple(arg.lower() for arg in oobstruct[1]), {})
else:
# cmdname, (args,), {kwargs}
return (oobstruct[0].lower(), tuple(arg.lower for arg in oobstruct[1]),
dict((key.lower(), val) for key, val in oobstruct[2].items()))
# either multiple funcnames or multiple func tuples; descend recursively
out = []
for oobpart in oobstruct:
out.append(self.oobstruct_parser(oobpart)[0])
return tuple(out)
def announce_all(self, message):
"""
Send message to all connected sessions
@ -379,5 +423,4 @@ class ServerSessionHandler(SessionHandler):
if session:
session.data_in(text=text, **kwargs)
SESSIONS = ServerSessionHandler()