mirror of
https://github.com/evennia/evennia.git
synced 2026-03-28 10:37:16 +01:00
Added MCCP (compression of data stream between server-client). Not fully functioning yet, tintin++ tends to complain of compression errors after a while (the server degrades back to uncompressed mode gracefully though). MCCP is thus deactivated in the server at the moment.
This commit is contained in:
parent
2104fd391b
commit
a4f8019c4a
3 changed files with 105 additions and 10 deletions
64
src/server/mccp.py
Normal file
64
src/server/mccp.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
|
||||
MCCP - Mud Client Compression Protocol
|
||||
|
||||
The implements the MCCP v2 telnet protocol as per
|
||||
http://tintin.sourceforge.net/mccp/. MCCP allows for the server to
|
||||
compress data when sending to supporting clients, reducing bandwidth
|
||||
by 70-90%.. The compression is done using Python's builtin zlib
|
||||
library. If the client doesn't support MCCP, server sends uncompressed
|
||||
instead. Note: On modern hardware you are not likely to notice the
|
||||
effect of MCCP unless you have extremely heavy traffic or sits on a
|
||||
terribly slow connection.
|
||||
|
||||
"""
|
||||
import zlib
|
||||
|
||||
# negotiations for v1 and v2 of the protocol
|
||||
MCCP = chr(86)
|
||||
FLUSH = zlib.Z_SYNC_FLUSH
|
||||
|
||||
def mccp_compress(protocol, data):
|
||||
"Handles zlib compression, if applicable"
|
||||
if hasattr(protocol, 'zlib'):
|
||||
data = protocol.zlib.compress(data)
|
||||
data += protocol.zlib.flush(FLUSH)
|
||||
return data
|
||||
|
||||
class Mccp(object):
|
||||
"""
|
||||
Implements the MCCP protocol. Add this to a
|
||||
variable on the telnet protocol to set it up.
|
||||
"""
|
||||
|
||||
def __init__(self, protocol):
|
||||
"""
|
||||
initialize MCCP by storing protocol on
|
||||
ourselves and calling the client to see if
|
||||
it supports MCCP. Sets callbacks to
|
||||
start zlib compression in that case.
|
||||
"""
|
||||
|
||||
self.protocol = protocol
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
# ask if client will mccp, connect callbacks to handle answer
|
||||
self.protocol.will(MCCP).addCallbacks(self.do_mccp, self.no_mccp)
|
||||
|
||||
def no_mccp(self, option):
|
||||
"""
|
||||
If client doesn't support mccp, don't do anything.
|
||||
"""
|
||||
print "deactivating mccp ..."
|
||||
if hasattr(self.protocol, 'zlib'):
|
||||
del self.protocol.zlib
|
||||
self.protocol.protocol_flags['MCCP'] = False
|
||||
|
||||
def do_mccp(self, option):
|
||||
"""
|
||||
The client supports MCCP. Set things up by
|
||||
creating a zlib compression stream.
|
||||
"""
|
||||
print "activating mccp ..."
|
||||
self.protocol.protocol_flags['MCCP'] = True
|
||||
self.protocol.requestNegotiation(MCCP, '')
|
||||
self.protocol.zlib = zlib.compressobj(9)
|
||||
|
|
@ -7,9 +7,9 @@ sessions etc.
|
|||
|
||||
"""
|
||||
|
||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE
|
||||
from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE, DO, DONT
|
||||
from src.server.session import Session
|
||||
from src.server import ttype
|
||||
from src.server import ttype, mccp
|
||||
from src.utils import utils, ansi
|
||||
|
||||
class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||
|
|
@ -27,8 +27,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
client_address = self.transport.client
|
||||
self.init_session("telnet", client_address, self.factory.sessionhandler)
|
||||
|
||||
# setup ttype
|
||||
self.ttype = ttype.Ttype(self)
|
||||
# setup ttype (client info)
|
||||
#self.ttype = ttype.Ttype(self)
|
||||
|
||||
# setup mccp (data compression)
|
||||
# self.mccp = mccp.Mccp(self) #TODO: mccp doesn't work quite right yet.
|
||||
|
||||
# add us to sessionhandler
|
||||
self.sessionhandler.connect(self)
|
||||
|
|
@ -38,7 +41,24 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
This sets up the options we allow for this protocol.
|
||||
"""
|
||||
return (option == LINEMODE or
|
||||
option == ttype.TTYPE)
|
||||
option == ttype.TTYPE or
|
||||
option == mccp.MCCP)
|
||||
|
||||
def enableLocal(self, option):
|
||||
"""
|
||||
Allow certain options on this protocol
|
||||
"""
|
||||
if option == mccp.MCCP:
|
||||
#self.mccp.do_mccp(option)
|
||||
return True
|
||||
|
||||
def disableLocal(self, option):
|
||||
if option == mccp.MCCP:
|
||||
self.mccp.no_mccp(option)
|
||||
return True
|
||||
else:
|
||||
return super(TelnetProtocol, self).disableLocal(option)
|
||||
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
|
|
@ -58,8 +78,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
# print "dataRcv:", data,
|
||||
# try:
|
||||
# for b in data:
|
||||
# print ord(b),
|
||||
# if b == chr(24): print "ttype found!"
|
||||
# print ord(b),
|
||||
# print ""
|
||||
# except Exception, e:
|
||||
# print str(e) + ":", str(data)
|
||||
|
|
@ -68,6 +87,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
|||
super(TelnetProtocol, self).dataReceived(data)
|
||||
else:
|
||||
StatefulTelnetProtocol.dataReceived(self, data)
|
||||
|
||||
def _write(self, byt):
|
||||
"hook overloading the one used in plain telnet"
|
||||
#print "_write (%s): %s" % (self.state, " ".join(str(ord(c)) for c in byt))
|
||||
super(TelnetProtocol, self)._write(mccp.mccp_compress(self, byt))
|
||||
|
||||
def sendLine(self, line):
|
||||
"hook overloading the one used linereceiver"
|
||||
#print "sendLine (%s):\n%s" % (self.state, line)
|
||||
super(TelnetProtocol, self).sendLine(mccp.mccp_compress(self, line))
|
||||
|
||||
def lineReceived(self, string):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
"""
|
||||
TTYPE (MTTS) - Mud Terminal Type Standard
|
||||
|
||||
This module implements the TTYPE telnet protocol as per
|
||||
http://tintin.sourceforge.net/mtts/. It allows the server to ask the
|
||||
client about its capabilities. If the client also supports TTYPE, it
|
||||
|
|
@ -42,9 +44,9 @@ class Ttype(object):
|
|||
self.protocol.protocol_flags['TTYPE'] = {}
|
||||
|
||||
# setup protocol to handle ttype initialization and negotiation
|
||||
self.protocol.negotiationMap[TTYPE] = self.negotiate_ttype
|
||||
self.protocol.negotiationMap[TTYPE] = self.do_ttype
|
||||
# ask if client will ttype, connect callback if it does.
|
||||
self.protocol.will(TTYPE).addCallbacks(self.negotiate_ttype, self.no_ttype)
|
||||
self.protocol.will(TTYPE).addCallbacks(self.do_ttype, self.no_ttype)
|
||||
|
||||
def no_ttype(self, option):
|
||||
"""
|
||||
|
|
@ -52,7 +54,7 @@ class Ttype(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def negotiate_ttype(self, option):
|
||||
def do_ttype(self, option):
|
||||
"""
|
||||
Handles negotiation of the ttype protocol once the
|
||||
client has confirmed that it supports the ttype
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue