Changed the OOB message structure to include sending text data as well; still not working fully.
This commit is contained in:
parent
529f13c689
commit
4817ec90b3
8 changed files with 249 additions and 240 deletions
|
|
@ -461,25 +461,34 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
return cmdhandler.cmdhandler(self, raw_string, callertype="object", session=session, **kwargs)
|
return cmdhandler.cmdhandler(self, raw_string, callertype="object", session=session, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Emits something to a session attached to the object.
|
Emits something to a session attached to the object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text (str, optional): The message to send
|
text (str or tuple, optional): The message to send. This
|
||||||
|
is treated internally like any send-command, so its
|
||||||
|
value can be a tuple if sending multiple arguments to
|
||||||
|
the `text` oob command.
|
||||||
from_obj (obj, optional): object that is sending. If
|
from_obj (obj, optional): object that is sending. If
|
||||||
given, at_msg_send will be called
|
given, at_msg_send will be called. This value will be
|
||||||
|
passed on to the protocol.
|
||||||
session (Session or list, optional): Session or list of
|
session (Session or list, optional): Session or list of
|
||||||
Sessions to relay data to, if any. If set, will
|
Sessions to relay data to, if any. If set, will force send
|
||||||
force send to these sessions. If unset, who receives the
|
to these sessions. If unset, who receives the message
|
||||||
message depends on the MULTISESSION_MODE.
|
depends on the MULTISESSION_MODE.
|
||||||
|
options (dict, optional): Message-specific option-value
|
||||||
|
pairs. These will be applied at the protocol level.
|
||||||
|
Kwargs:
|
||||||
|
any (string or tuples): All kwarg keys not listed above
|
||||||
|
will be treated as send-command names and their arguments
|
||||||
|
(which can be a string or a tuple).
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
`at_msg_receive` will be called on this Object.
|
`at_msg_receive` will be called on this Object.
|
||||||
All extra kwargs will be passed on to the protocol.
|
All extra kwargs will be passed on to the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = to_str(text, force_string=True) if text != None else ""
|
|
||||||
# try send hooks
|
# try send hooks
|
||||||
if from_obj:
|
if from_obj:
|
||||||
try:
|
try:
|
||||||
|
|
@ -493,10 +502,12 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
||||||
|
kwargs["options"] = options
|
||||||
|
|
||||||
# relay to session(s)
|
# relay to session(s)
|
||||||
sessions = make_iter(session) if session else self.sessions.all()
|
sessions = make_iter(session) if session else self.sessions.all()
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
session.msg(text=text, **kwargs)
|
session.data_out(text=text, **kwargs)
|
||||||
|
|
||||||
def for_contents(self, func, exclude=None, **kwargs):
|
def for_contents(self, func, exclude=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
|
|
||||||
* Helper methods
|
* Helper methods
|
||||||
|
|
||||||
- msg(outgoing_string, from_obj=None, **kwargs)
|
- msg(text=None, from_obj=None, session=None, options=None, **kwargs)
|
||||||
- execute_cmd(raw_string)
|
- execute_cmd(raw_string)
|
||||||
- search(ostring, global_search=False, attribute_name=None,
|
- search(ostring, global_search=False, attribute_name=None,
|
||||||
use_nicks=False, location=None,
|
use_nicks=False, location=None,
|
||||||
|
|
@ -384,7 +384,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
super(PlayerDB, self).delete(*args, **kwargs)
|
super(PlayerDB, self).delete(*args, **kwargs)
|
||||||
## methods inherited from database model
|
## methods inherited from database model
|
||||||
|
|
||||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Evennia -> User
|
Evennia -> User
|
||||||
This is the main route for sending data back to the user from the
|
This is the main route for sending data back to the user from the
|
||||||
|
|
@ -398,12 +398,11 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
Sessions to receive this send. If given, overrules the
|
Sessions to receive this send. If given, overrules the
|
||||||
default send behavior for the current
|
default send behavior for the current
|
||||||
MULTISESSION_MODE.
|
MULTISESSION_MODE.
|
||||||
Notes:
|
options (list): Protocol-specific options. Passed on to the protocol.
|
||||||
All other keywords are passed on to the protocol.
|
Kwargs:
|
||||||
|
any (dict): All other keywords are passed on to the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
text = to_str(text, force_string=True) if text else ""
|
|
||||||
|
|
||||||
if from_obj:
|
if from_obj:
|
||||||
# call hook
|
# call hook
|
||||||
try:
|
try:
|
||||||
|
|
@ -414,7 +413,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
|
||||||
# session relay
|
# session relay
|
||||||
sessions = make_iter(session) if session else self.sessions.all()
|
sessions = make_iter(session) if session else self.sessions.all()
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
session.msg(text=text, **kwargs)
|
session.data_out(text=text, **kwargs)
|
||||||
|
|
||||||
def execute_cmd(self, raw_string, session=None, **kwargs):
|
def execute_cmd(self, raw_string, session=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -424,20 +424,19 @@ class AMPProtocol(amp.AMP):
|
||||||
self.factory.server.sessions.data_in(self.factory.server.sessions[sessid], **kwargs)
|
self.factory.server.sessions.data_in(self.factory.server.sessions[sessid], **kwargs)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def send_MsgPortal2Server(self, session, text="", **kwargs):
|
def send_MsgPortal2Server(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Access method called by the Portal and executed on the Portal.
|
Access method called by the Portal and executed on the Portal.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sessid (int): Unique Session id.
|
sessid (int): Unique Session id.
|
||||||
text (str): Message to send over the wire.
|
|
||||||
kwargs (any, optional): Optional data.
|
kwargs (any, optional): Optional data.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
deferred (Deferred): Asynchronous return.
|
deferred (Deferred): Asynchronous return.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.send_data(MsgPortal2Server, session.sessid, text=text, **kwargs)
|
return self.send_data(MsgPortal2Server, session.sessid, **kwargs)
|
||||||
|
|
||||||
# Server -> Portal message
|
# Server -> Portal message
|
||||||
|
|
||||||
|
|
@ -455,18 +454,17 @@ class AMPProtocol(amp.AMP):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def send_MsgServer2Portal(self, session, text="", **kwargs):
|
def send_MsgServer2Portal(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Access method - executed on the Server for sending data
|
Access method - executed on the Server for sending data
|
||||||
to Portal.
|
to Portal.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
session (Session): Unique Session.
|
session (Session): Unique Session.
|
||||||
msg (str, optional): Message to send over the wire.
|
|
||||||
kwargs (any, optiona): Extra data.
|
kwargs (any, optiona): Extra data.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.send_data(MsgServer2Portal, session.sessid, text=text, **kwargs)
|
return self.send_data(MsgServer2Portal, session.sessid, **kwargs)
|
||||||
|
|
||||||
# Server administration from the Portal side
|
# Server administration from the Portal side
|
||||||
@AdminPortal2Server.responder
|
@AdminPortal2Server.responder
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from collections import deque
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
||||||
|
from evennia.utils.logger import log_trace
|
||||||
|
|
||||||
# module import
|
# module import
|
||||||
_MOD_IMPORT = None
|
_MOD_IMPORT = None
|
||||||
|
|
@ -295,74 +296,9 @@ class PortalSessionHandler(SessionHandler):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for session in self.values():
|
for session in self.values():
|
||||||
session.data_out(message)
|
session.data_out(text=message)
|
||||||
|
|
||||||
def oobstruct_parser(self, oobstruct):
|
def data_in(self, session, **kwargs):
|
||||||
"""
|
|
||||||
Helper method for each session to use to parse oob structures
|
|
||||||
(The 'oob' kwarg of the msg() method).
|
|
||||||
|
|
||||||
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)
|
|
||||||
if not oobstruct:
|
|
||||||
return tuple(None, (), {})
|
|
||||||
elif not hasattr(oobstruct, "__iter__"):
|
|
||||||
# a singular command name, without arguments or kwargs
|
|
||||||
return (oobstruct, (), {})
|
|
||||||
# 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], (), {})
|
|
||||||
elif slen == 2:
|
|
||||||
if isinstance(oobstruct[1], dict):
|
|
||||||
# (cmdname, {kwargs})
|
|
||||||
return (oobstruct[0], (), dict(oobstruct[1]))
|
|
||||||
elif isinstance(oobstruct[1], (tuple, list)):
|
|
||||||
# (cmdname, (args,))
|
|
||||||
return (oobstruct[0], tuple(oobstruct[1]), {})
|
|
||||||
else:
|
|
||||||
# (cmdname, arg)
|
|
||||||
return (oobstruct[0], (oobstruct[1],), {})
|
|
||||||
else:
|
|
||||||
# (cmdname, (args,), {kwargs})
|
|
||||||
return (oobstruct[0], tuple(oobstruct[1]), dict(oobstruct[2]))
|
|
||||||
|
|
||||||
if hasattr(oobstruct, "__iter__"):
|
|
||||||
# differentiate between (cmdname, cmdname),
|
|
||||||
# (cmdname, (args), {kwargs}) and ((cmdname,(args),{kwargs}),
|
|
||||||
# (cmdname,(args),{kwargs}), ...)
|
|
||||||
|
|
||||||
if oobstruct and isinstance(oobstruct[0], basestring):
|
|
||||||
return (list(_parse(oobstruct)),)
|
|
||||||
else:
|
|
||||||
out = []
|
|
||||||
for oobpart in oobstruct:
|
|
||||||
out.append(_parse(oobpart))
|
|
||||||
return (list(out),)
|
|
||||||
return (_parse(oobstruct),)
|
|
||||||
|
|
||||||
|
|
||||||
def data_in(self, session, text="", **kwargs):
|
|
||||||
"""
|
"""
|
||||||
Called by portal sessions for relaying data coming
|
Called by portal sessions for relaying data coming
|
||||||
in from the protocol to the server.
|
in from the protocol to the server.
|
||||||
|
|
@ -371,7 +307,6 @@ class PortalSessionHandler(SessionHandler):
|
||||||
session (PortalSession): Session receiving data.
|
session (PortalSession): Session receiving data.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Text from protocol.
|
|
||||||
kwargs (any): Other data from protocol.
|
kwargs (any): Other data from protocol.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
@ -394,11 +329,13 @@ class PortalSessionHandler(SessionHandler):
|
||||||
if self.command_overflow:
|
if self.command_overflow:
|
||||||
self.data_out(session, text=_ERROR_COMMAND_OVERFLOW)
|
self.data_out(session, text=_ERROR_COMMAND_OVERFLOW)
|
||||||
return
|
return
|
||||||
|
# scrub data
|
||||||
|
kwargs = self.clean_senddata(session, kwargs)
|
||||||
|
|
||||||
# relay data to Server
|
# relay data to Server
|
||||||
self.command_counter += 1
|
self.command_counter += 1
|
||||||
session.cmd_last = now
|
session.cmd_last = now
|
||||||
self.portal.amp_protocol.send_MsgPortal2Server(session,
|
self.portal.amp_protocol.send_MsgPortal2Server(session,
|
||||||
text=text,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
else:
|
else:
|
||||||
# called by the callLater callback
|
# called by the callLater callback
|
||||||
|
|
@ -406,12 +343,10 @@ class PortalSessionHandler(SessionHandler):
|
||||||
self.command_overflow = False
|
self.command_overflow = False
|
||||||
reactor.callLater(1.0, self.data_in, None)
|
reactor.callLater(1.0, self.data_in, None)
|
||||||
|
|
||||||
|
def data_out(self, session, **kwargs):
|
||||||
def data_out(self, session, text=None, **kwargs):
|
|
||||||
"""
|
"""
|
||||||
Called by server for having the portal relay messages and data
|
Called by server for having the portal relay messages and data
|
||||||
to the correct session protocol. We also convert oob input to
|
to the correct session protocol.
|
||||||
a generic form here.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
session (Session): Session sending data.
|
session (Session): Session sending data.
|
||||||
|
|
@ -424,10 +359,16 @@ class PortalSessionHandler(SessionHandler):
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
#from evennia.server.profiling.timetrace import timetrace
|
||||||
#text = timetrace(text, "portalsessionhandler.data_out")
|
#text = timetrace(text, "portalsessionhandler.data_out")
|
||||||
|
|
||||||
|
# distribute outgoing data to the correct session methods.
|
||||||
if session:
|
if session:
|
||||||
# convert oob to the generic format
|
print ("portalsessionhandler.data_out:", session, kwargs, session.datamap)
|
||||||
if "oob" in kwargs:
|
for cmdname, args in kwargs.items():
|
||||||
kwargs["oob"] = self.oobstruct_parser(kwargs["oob"])
|
try:
|
||||||
session.data_out(text=text, **kwargs)
|
if cmdname in session.datamap:
|
||||||
|
session.datamap[cmdname](session, *args)
|
||||||
|
else:
|
||||||
|
session.datamap["_default"](session, *args)
|
||||||
|
except Exception:
|
||||||
|
log_trace()
|
||||||
|
|
||||||
PORTAL_SESSIONS = PortalSessionHandler()
|
PORTAL_SESSIONS = PortalSessionHandler()
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ sessions etc.
|
||||||
import re
|
import re
|
||||||
from twisted.internet.task import LoopingCall
|
from twisted.internet.task import LoopingCall
|
||||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, GA, WILL, WONT, ECHO
|
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, GA, WILL, WONT, ECHO
|
||||||
|
from django.conf import settings
|
||||||
from evennia.server.session import Session
|
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
|
||||||
|
|
@ -21,6 +22,7 @@ NOP = chr(241)
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\{n$")
|
||||||
_RE_LEND = re.compile(r"\n$|\r$", re.MULTILINE)
|
_RE_LEND = re.compile(r"\n$|\r$", re.MULTILINE)
|
||||||
|
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||||
|
|
||||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
"""
|
"""
|
||||||
|
|
@ -69,6 +71,10 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.keep_alive = LoopingCall(self._write, IAC + NOP)
|
self.keep_alive = LoopingCall(self._write, IAC + NOP)
|
||||||
self.keep_alive.start(30, now=False)
|
self.keep_alive.start(30, now=False)
|
||||||
|
|
||||||
|
self.datamap = {"text": self.send_text,
|
||||||
|
"prompt": self.send_prompt,
|
||||||
|
"_default": self.send_oob}
|
||||||
|
|
||||||
|
|
||||||
def handshake_done(self, force=False):
|
def handshake_done(self, force=False):
|
||||||
"""
|
"""
|
||||||
|
|
@ -242,98 +248,117 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.data_out(reason)
|
self.data_out(reason)
|
||||||
self.connectionLost(reason)
|
self.connectionLost(reason)
|
||||||
|
|
||||||
def data_in(self, text=None, **kwargs):
|
def data_in(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Data User -> Evennia
|
Data User -> Evennia
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Incoming text.
|
|
||||||
kwargs (any): Options from the protocol.
|
kwargs (any): Options from the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
#from evennia.server.profiling.timetrace import timetrace
|
||||||
#text = timetrace(text, "telnet.data_in")
|
#text = timetrace(text, "telnet.data_in")
|
||||||
|
|
||||||
self.sessionhandler.data_in(self, text=text, **kwargs)
|
self.sessionhandler.data_in(self, **kwargs)
|
||||||
|
|
||||||
def data_out(self, text=None, **kwargs):
|
def data_out(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Data Evennia -> User. A generic hook method for engine to call
|
Data Evennia -> User
|
||||||
in order to send data through the telnet connection.
|
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Text to send.
|
kwargs (any): Options to the protocol
|
||||||
oob (list): `[(cmdname,args,kwargs), ...]`, supply an
|
"""
|
||||||
Out-of-Band instruction.
|
self.sessionhandler.data_out(self, **kwargs)
|
||||||
xterm256 (bool): Enforce xterm256 setting. If not given,
|
|
||||||
ttype result is used. If client does not suport xterm256,
|
|
||||||
the ansi fallback will be used
|
|
||||||
mxp (bool): Enforce mxp setting. If not given, enables if
|
|
||||||
we detected client support for it
|
|
||||||
ansi (bool): Enforce ansi setting. If not given, ttype
|
|
||||||
result is used.
|
|
||||||
nomarkup (bool): If True, strip all ansi markup (this is
|
|
||||||
the same as `xterm256=False, ansi=False`)
|
|
||||||
raw (bool):Pass string through without any ansi processing
|
|
||||||
(i.e. include Evennia ansi markers but do not convert them
|
|
||||||
into ansi tokens)
|
|
||||||
prompt (str): Supply a prompt text which gets sent without
|
|
||||||
a newline added to the end.
|
|
||||||
echo (str): Turn on/off line echo on the client, if the
|
|
||||||
client supports it (e.g. for password input). Remember
|
|
||||||
that you must manually activate it again later.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
The telnet TTYPE negotiation flags, if any, are used if no kwargs
|
@staticmethod
|
||||||
are given.
|
def send_text(session, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Send text data. This is an in-band telnet operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The first argument is always the text string to send. No other arguments
|
||||||
|
are considered.
|
||||||
|
*options (str): All other arguments are considered option flags.
|
||||||
|
Available flags are (if not set, TTYPE will be used, turning on if available):
|
||||||
|
mxp: Enforce MXP link support.
|
||||||
|
ansi: Enforce no ANSI colors.
|
||||||
|
xterm256: Enforce xterm256 colors, regardless of TTYPE.
|
||||||
|
noxterm256: Enforce no xterm256 color support, regardless of TTYPE.
|
||||||
|
nomarkup: Strip all ANSI markup. This is the same as noxterm256,noansi
|
||||||
|
raw: Pass string through without any ansi processing
|
||||||
|
(i.e. include Evennia ansi markers but do not
|
||||||
|
convert them into ansi tokens)
|
||||||
|
echo: Turn on/off line echo on the client. Turn
|
||||||
|
off line echo for client, for example for password.
|
||||||
|
Note that it must be actively turned back on again!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
## profiling, debugging
|
if args:
|
||||||
#if text.startswith("TEST_MESSAGE"): 1/0
|
text = args[0]
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
if text is None:
|
||||||
#text = timetrace(text, "telnet.data_out", final=True)
|
|
||||||
|
|
||||||
try:
|
|
||||||
text = utils.to_str(text if text else "", encoding=self.encoding)
|
|
||||||
except Exception as e:
|
|
||||||
self.sendLine(str(e))
|
|
||||||
return
|
return
|
||||||
if "oob" in kwargs and "OOB" in self.protocol_flags:
|
|
||||||
# oob is a list of [(cmdname, arg, kwarg), ...]
|
|
||||||
for cmdname, args, okwargs in kwargs["oob"]:
|
|
||||||
self.oob.data_out(cmdname, *args, **okwargs)
|
|
||||||
|
|
||||||
# parse **kwargs, falling back to ttype if nothing is given explicitly
|
# handle arguments
|
||||||
ttype = self.protocol_flags.get('TTYPE', {})
|
options = kwargs.get("options", {})
|
||||||
xterm256 = kwargs.get("xterm256", ttype.get('256 COLORS', False) if ttype.get("init_done") else True)
|
ttype = session.protocol_flags.get('TTYPE', {})
|
||||||
useansi = kwargs.get("ansi", ttype and ttype.get('ANSI', False) if ttype.get("init_done") else True)
|
xterm256 = options.get("xterm256", ttype.get('256 COLORS', False) if ttype.get("init_done") else True)
|
||||||
raw = kwargs.get("raw", False)
|
useansi = options.get("ansi", ttype and ttype.get('ANSI', False) if ttype.get("init_done") else True)
|
||||||
nomarkup = kwargs.get("nomarkup", not (xterm256 or useansi))
|
raw = options.get("raw", False)
|
||||||
prompt = kwargs.get("prompt")
|
nomarkup = options.get("nomarkup", not (xterm256 or useansi))
|
||||||
echo = kwargs.get("echo", None)
|
echo = options.get("echo", None)
|
||||||
mxp = kwargs.get("mxp", self.protocol_flags.get("MXP", False))
|
mxp = options.get("mxp", session.protocol_flags.get("MXP", False))
|
||||||
|
screenreader = options.get("screenreader", session.screenreader)
|
||||||
|
|
||||||
|
if screenreader:
|
||||||
|
# screenreader mode cleans up output
|
||||||
|
text = ansi.parse_ansi(text, strip_ansi=True, xterm256=False, mxp=False)
|
||||||
|
text = _RE_SCREENREADER_REGEX.sub("", text)
|
||||||
|
|
||||||
|
if options.get("send_prompt"):
|
||||||
|
# send a prompt instead.
|
||||||
|
if not raw:
|
||||||
|
# processing
|
||||||
|
prompt = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256)
|
||||||
|
if mxp:
|
||||||
|
prompt = mxp_parse(prompt)
|
||||||
|
prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
||||||
|
prompt += IAC + GA
|
||||||
|
session.transport.write(mccp_compress(session, prompt))
|
||||||
|
else:
|
||||||
if raw:
|
if raw:
|
||||||
# no processing whatsoever
|
# no processing
|
||||||
self.sendLine(text)
|
session.sendLine(text)
|
||||||
elif text:
|
return
|
||||||
|
else:
|
||||||
# we need to make sure to kill the color at the end in order
|
# we need to make sure to kill the color at the end in order
|
||||||
# to match the webclient output.
|
# to match the webclient output.
|
||||||
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256, mxp=mxp)
|
linetosend = ansi.parse_ansi(_RE_N.sub("", text) + "{n", strip_ansi=nomarkup, xterm256=xterm256, mxp=mxp)
|
||||||
if mxp:
|
if mxp:
|
||||||
linetosend = mxp_parse(linetosend)
|
linetosend = mxp_parse(linetosend)
|
||||||
self.sendLine(linetosend)
|
session.sendLine(linetosend)
|
||||||
|
|
||||||
if prompt:
|
if echo is not None:
|
||||||
# Send prompt separately
|
# turn on/off echo
|
||||||
prompt = ansi.parse_ansi(_RE_N.sub("", prompt) + "{n", strip_ansi=nomarkup, xterm256=xterm256)
|
|
||||||
if mxp:
|
|
||||||
prompt = mxp_parse(prompt)
|
|
||||||
prompt = prompt.replace(IAC, IAC + IAC).replace('\n', '\r\n')
|
|
||||||
prompt += IAC + GA
|
|
||||||
self.transport.write(mccp_compress(self, prompt))
|
|
||||||
if echo:
|
if echo:
|
||||||
self.transport.write(mccp_compress(self, IAC+WONT+ECHO))
|
session.transport.write(mccp_compress(session, IAC+WILL+ECHO))
|
||||||
elif echo == False:
|
else:
|
||||||
self.transport.write(mccp_compress(self, IAC+WILL+ECHO))
|
session.transport.write(mccp_compress(session, IAC+WONT+ECHO))
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send_prompt(session, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Send a prompt - a text without a line end. See send_text for argument options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
kwargs["options"].update({"send_prompt": True})
|
||||||
|
session.send_text(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def send_oob(session, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Send oob data
|
||||||
|
"""
|
||||||
|
print "telnet.send_oob not implemented yet! ", args
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ _SA = object.__setattr__
|
||||||
_ObjectDB = None
|
_ObjectDB = None
|
||||||
_ANSI = None
|
_ANSI = None
|
||||||
_INLINEFUNC_ENABLED = settings.INLINEFUNC_ENABLED
|
_INLINEFUNC_ENABLED = settings.INLINEFUNC_ENABLED
|
||||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
@ -169,6 +168,8 @@ class ServerSession(Session):
|
||||||
self.player = None
|
self.player = None
|
||||||
self.cmdset_storage_string = ""
|
self.cmdset_storage_string = ""
|
||||||
self.cmdset = CmdSetHandler(self, True)
|
self.cmdset = CmdSetHandler(self, True)
|
||||||
|
self.datamap = {"text": self.recv_text,
|
||||||
|
"_default": self.recv_text}
|
||||||
|
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
return [path.strip() for path in self.cmdset_storage_string.split(',')]
|
||||||
|
|
@ -336,22 +337,22 @@ class ServerSession(Session):
|
||||||
# Player-visible idle time, not used in idle timeout calcs.
|
# Player-visible idle time, not used in idle timeout calcs.
|
||||||
self.cmd_last_visible = self.cmd_last
|
self.cmd_last_visible = self.cmd_last
|
||||||
|
|
||||||
def data_in(self, text=None, **kwargs):
|
@staticmethod
|
||||||
|
def recv_text(session, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Send data User->Evennia. This will in effect execute a command
|
Recv command data User->Evennia. This will in effect execute a command
|
||||||
string on the server.
|
string on the server.
|
||||||
|
|
||||||
Note that oob data is already sent separately to the
|
Args:
|
||||||
oobhandler at this point.
|
text (str): First arg is used as text-command input. Other
|
||||||
|
arguments are ignored.
|
||||||
Kwargs:
|
|
||||||
text (str): A text to relay
|
|
||||||
kwargs (any): Other parameters from the protocol.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
#from evennia.server.profiling.timetrace import timetrace
|
||||||
#text = timetrace(text, "ServerSession.data_in")
|
#text = timetrace(text, "ServerSession.data_in")
|
||||||
|
|
||||||
|
text = args[0] if args else None
|
||||||
|
|
||||||
#explicitly check for None since text can be an empty string, which is
|
#explicitly check for None since text can be an empty string, which is
|
||||||
#also valid
|
#also valid
|
||||||
if text is not None:
|
if text is not None:
|
||||||
|
|
@ -359,47 +360,47 @@ class ServerSession(Session):
|
||||||
#text = to_unicode(escape_control_sequences(text), encoding=self.encoding)
|
#text = to_unicode(escape_control_sequences(text), encoding=self.encoding)
|
||||||
# handle the 'idle' command
|
# handle the 'idle' command
|
||||||
if text.strip() == _IDLE_COMMAND:
|
if text.strip() == _IDLE_COMMAND:
|
||||||
self.update_session_counters(idle=True)
|
session.update_session_counters(idle=True)
|
||||||
return
|
return
|
||||||
if self.player:
|
if session.player:
|
||||||
# nick replacement
|
# nick replacement
|
||||||
puppet = self.puppet
|
puppet = session.puppet
|
||||||
if puppet:
|
if puppet:
|
||||||
text = puppet.nicks.nickreplace(text,
|
text = puppet.nicks.nickreplace(text,
|
||||||
categories=("inputline", "channel"), include_player=True)
|
categories=("inputline", "channel"), include_player=True)
|
||||||
else:
|
else:
|
||||||
text = self.player.nicks.nickreplace(text,
|
text = session.player.nicks.nickreplace(text,
|
||||||
categories=("inputline", "channels"), include_player=False)
|
categories=("inputline", "channels"), include_player=False)
|
||||||
cmdhandler(self, text, callertype="session", session=self)
|
cmdhandler(session, text, callertype="session", session=session)
|
||||||
self.update_session_counters()
|
session.update_session_counters()
|
||||||
|
|
||||||
execute_cmd = data_in # alias
|
|
||||||
|
|
||||||
def data_out(self, text=None, **kwargs):
|
def data_out(self, text=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Send Evennia -> User
|
Sending data from Evennia->Player
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): A text to relay
|
text (str or tuple)
|
||||||
kwargs (any): Other parameters to the protocol.
|
any (str or tuple): Send-commands identified
|
||||||
|
by their keys. Or "options", carrying options
|
||||||
|
for the protocol(s).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
print "serversession.data_out:", text, kwargs
|
||||||
#text = timetrace(text, "ServerSession.data_out")
|
if text:
|
||||||
|
if hasattr(text, "__iter__"):
|
||||||
|
text, args = text[0], list(text[1:])
|
||||||
|
else:
|
||||||
|
text, args = text, []
|
||||||
|
options = kwargs.get("options", {})
|
||||||
|
raw = options.get("raw", False)
|
||||||
|
strip_inlinefunc = options.get("strip_inlinefunc", False)
|
||||||
|
if _INLINEFUNC_ENABLED and not raw:
|
||||||
|
text = parse_inlinefunc(text, strip=strip_inlinefunc, session=self)
|
||||||
|
text = parse_nested_inlinefunc(text, strip=strip_inlinefunc, session=self)
|
||||||
|
text = [text] + args
|
||||||
|
|
||||||
text = text if text else ""
|
|
||||||
if _INLINEFUNC_ENABLED and not "raw" in kwargs:
|
|
||||||
text = parse_inlinefunc(text, strip="strip_inlinefunc" in kwargs, session=self)
|
|
||||||
text = parse_nested_inlinefunc(text, strip="strip_inlinefunc" in kwargs, session=self)
|
|
||||||
if self.screenreader:
|
|
||||||
global _ANSI
|
|
||||||
if not _ANSI:
|
|
||||||
from evennia.utils import ansi as _ANSI
|
|
||||||
text = _ANSI.parse_ansi(text, strip_ansi=True, xterm256=False, mxp=False)
|
|
||||||
text = _RE_SCREENREADER_REGEX.sub("", text)
|
|
||||||
self.sessionhandler.data_out(self, text=text, **kwargs)
|
self.sessionhandler.data_out(self, text=text, **kwargs)
|
||||||
# alias
|
msg = data_out # alias
|
||||||
msg = data_out
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"Handle session comparisons"
|
"Handle session comparisons"
|
||||||
|
|
@ -427,6 +428,7 @@ class ServerSession(Session):
|
||||||
"Unicode representation"
|
"Unicode representation"
|
||||||
return u"%s" % str(self)
|
return u"%s" % str(self)
|
||||||
|
|
||||||
|
|
||||||
# Dummy API hooks for use during non-loggedin operation
|
# Dummy API hooks for use during non-loggedin operation
|
||||||
|
|
||||||
def at_cmdset_get(self, **kwargs):
|
def at_cmdset_get(self, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@ class Session(object):
|
||||||
self.protocol_flags = {}
|
self.protocol_flags = {}
|
||||||
self.server_data = {}
|
self.server_data = {}
|
||||||
|
|
||||||
|
# map of input data to session methods
|
||||||
|
self.datamap = {}
|
||||||
|
|
||||||
# a back-reference to the relevant sessionhandler this
|
# a back-reference to the relevant sessionhandler this
|
||||||
# session is stored in.
|
# session is stored in.
|
||||||
self.sessionhandler = sessionhandler
|
self.sessionhandler = sessionhandler
|
||||||
|
|
@ -135,25 +138,23 @@ class Session(object):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def data_out(self, text=None, **kwargs):
|
def data_out(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Generic hook for sending data out through the protocol. Server
|
Generic hook for sending data out through the protocol. Server
|
||||||
protocols can use this right away. Portal sessions
|
protocols can use this right away. Portal sessions
|
||||||
should overload this to format/handle the outgoing data as needed.
|
should overload this to format/handle the outgoing data as needed.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Text data
|
|
||||||
kwargs (any): Other data to the protocol.
|
kwargs (any): Other data to the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def data_in(self, text=None, **kwargs):
|
def data_in(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook for protocols to send incoming data to the engine.
|
Hook for protocols to send incoming data to the engine.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Text data
|
|
||||||
kwargs (any): Other data from the protocol.
|
kwargs (any): Other data from the protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from future.utils import listvalues
|
||||||
from time import time
|
from time import time
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
from evennia.commands.cmdhandler import CMD_LOGINSTART
|
||||||
|
from evennia.utils.logger import log_trace
|
||||||
from evennia.utils.utils import variable_from_module, is_iter, \
|
from evennia.utils.utils import variable_from_module, is_iter, \
|
||||||
to_str, to_unicode, strip_control_sequences, make_iter
|
to_str, to_unicode, strip_control_sequences, make_iter
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ _MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||||
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
_IDLE_TIMEOUT = settings.IDLE_TIMEOUT
|
||||||
_MAX_SERVER_COMMANDS_PER_SECOND = 100.0
|
_MAX_SERVER_COMMANDS_PER_SECOND = 100.0
|
||||||
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0
|
_MAX_SESSION_COMMANDS_PER_SECOND = 5.0
|
||||||
|
_MODEL_MAP = None
|
||||||
|
|
||||||
def delayed_import():
|
def delayed_import():
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,6 +117,47 @@ class SessionHandler(dict):
|
||||||
"""
|
"""
|
||||||
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.items())
|
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.items())
|
||||||
|
|
||||||
|
def clean_senddata(self, session, kwargs):
|
||||||
|
"""
|
||||||
|
Clean up data for sending across the AMP wire.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session (Session): The relevant session instance.
|
||||||
|
kwargs (dict): Every keyword represents a send-instruction.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
kwargs (dict): A cleaned dictionary of cmdname:args pairs,
|
||||||
|
where the keys and args have all been converted to
|
||||||
|
send-safe entities (strings or numbers).
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _validate(data):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
newdict = {}
|
||||||
|
for key, part in data.items():
|
||||||
|
newdict[key] = _validate(part)
|
||||||
|
return newdict
|
||||||
|
elif hasattr(data, "__iter__"):
|
||||||
|
return [_validate(part) for part in data]
|
||||||
|
elif isinstance(data, basestring):
|
||||||
|
try:
|
||||||
|
return data and to_str(to_unicode(data), encoding=session.encoding)
|
||||||
|
except LookupError:
|
||||||
|
# wrong encoding set on the session. Set it to a safe one
|
||||||
|
session.encoding = "utf-8"
|
||||||
|
return to_str(to_unicode(data), encoding=session.encoding)
|
||||||
|
elif hasattr(data, "id") and hasattr(data, "db_date_created") and hasattr(data, '__dbclass__'):
|
||||||
|
# convert database-object to their string representation.
|
||||||
|
return _validate(unicode(data))
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
clean_kwargs = {"options":kwargs.pop("options", {})}
|
||||||
|
for key in kwargs:
|
||||||
|
args = _validate(kwargs[key])
|
||||||
|
clean_kwargs[_validate(key)] = (args,) if args is not None and \
|
||||||
|
not hasattr(args, "__iter__") else args
|
||||||
|
return clean_kwargs
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Server-SessionHandler class
|
# Server-SessionHandler class
|
||||||
|
|
@ -170,7 +212,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
# validate all scripts
|
# validate all scripts
|
||||||
_ScriptDB.objects.validate()
|
_ScriptDB.objects.validate()
|
||||||
self[sess.sessid] = sess
|
self[sess.sessid] = sess
|
||||||
sess.data_in(CMD_LOGINSTART)
|
sess.data_in(text=CMD_LOGINSTART)
|
||||||
|
|
||||||
def portal_session_sync(self, portalsessiondata):
|
def portal_session_sync(self, portalsessiondata):
|
||||||
"""
|
"""
|
||||||
|
|
@ -498,7 +540,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
for sess in self.values():
|
for sess in self.values():
|
||||||
self.data_out(sess, message)
|
self.data_out(sess, message)
|
||||||
|
|
||||||
def data_out(self, session, text="", **kwargs):
|
def data_out(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Sending data Server -> Portal
|
Sending data Server -> Portal
|
||||||
|
|
||||||
|
|
@ -506,24 +548,18 @@ class ServerSessionHandler(SessionHandler):
|
||||||
session (Session): Session to relay to.
|
session (Session): Session to relay to.
|
||||||
text (str, optional): text data to return
|
text (str, optional): text data to return
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The outdata will be scrubbed for sending across
|
||||||
|
the wire here.
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
# clean output for sending
|
||||||
#text = timetrace(text, "ServerSessionHandler.data_out")
|
kwargs = self.clean_senddata(session, kwargs)
|
||||||
|
|
||||||
try:
|
|
||||||
text = text and to_str(to_unicode(text), encoding=session.encoding)
|
|
||||||
except LookupError:
|
|
||||||
# wrong encoding set on the session. Set it to a safe one
|
|
||||||
session.encoding = "utf-8"
|
|
||||||
text = to_str(to_unicode(text), encoding=session.encoding)
|
|
||||||
|
|
||||||
|
|
||||||
# send across AMP
|
# send across AMP
|
||||||
|
print "sessionhandler.data_out:", kwargs
|
||||||
self.server.amp_protocol.send_MsgServer2Portal(session,
|
self.server.amp_protocol.send_MsgServer2Portal(session,
|
||||||
text=text,
|
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
def data_in(self, session, text="", **kwargs):
|
def data_in(self, session, **kwargs):
|
||||||
"""
|
"""
|
||||||
Data Portal -> Server.
|
Data Portal -> Server.
|
||||||
We also intercept OOB communication here.
|
We also intercept OOB communication here.
|
||||||
|
|
@ -532,25 +568,21 @@ class ServerSessionHandler(SessionHandler):
|
||||||
sessions (Session): Session.
|
sessions (Session): Session.
|
||||||
|
|
||||||
Kwargs:
|
Kwargs:
|
||||||
text (str): Text from protocol.
|
|
||||||
kwargs (any): Other data from protocol.
|
kwargs (any): Other data from protocol.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#from evennia.server.profiling.timetrace import timetrace
|
|
||||||
#text = timetrace(text, "ServerSessionHandler.data_in")
|
|
||||||
if session:
|
|
||||||
text = text and to_unicode(strip_control_sequences(text), encoding=session.encoding)
|
|
||||||
if "oob" in kwargs:
|
|
||||||
# incoming data is always on the form (cmdname, args, kwargs)
|
|
||||||
global _OOB_HANDLER
|
|
||||||
if not _OOB_HANDLER:
|
|
||||||
from evennia.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
|
|
||||||
funcname, args, kwargs = kwargs.pop("oob")
|
|
||||||
if funcname:
|
|
||||||
_OOB_HANDLER.execute_cmd(session, funcname, *args, **kwargs)
|
|
||||||
|
|
||||||
# pass the rest off to the session
|
# distribute incoming data to the correct receiving methods.
|
||||||
session.data_in(text=text, **kwargs)
|
if session:
|
||||||
|
for cmdname, args in kwargs.items():
|
||||||
|
try:
|
||||||
|
if cmdname in session.datamap:
|
||||||
|
print "sessionhandler: data_in", cmdname, args
|
||||||
|
session.datamap[cmdname](session, *args)
|
||||||
|
else:
|
||||||
|
session.datamap["_default"](session, *args)
|
||||||
|
except Exception:
|
||||||
|
log_trace()
|
||||||
|
|
||||||
SESSION_HANDLER = ServerSessionHandler()
|
SESSION_HANDLER = ServerSessionHandler()
|
||||||
SESSIONS = SESSION_HANDLER # legacy
|
SESSIONS = SESSION_HANDLER # legacy
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue