diff --git a/src/server/server.py b/src/server/server.py index 4427b354f7..18dc1f498c 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -158,7 +158,7 @@ class Evennia(object): """ Outputs server startup info to the terminal. """ - print _(' %(servername)s Portal (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION} + print _(' %(servername)s Server (%(version)s) started.') % {'servername': SERVERNAME, 'version': VERSION} print ' amp (Portal): %s' % AMP_PORT def set_restart_mode(self, mode=None): diff --git a/src/server/session.py b/src/server/session.py index ed954c0bdf..8db2a7789b 100644 --- a/src/server/session.py +++ b/src/server/session.py @@ -35,8 +35,8 @@ class Session(object): # names of attributes that should be affected by syncing. _attrs_to_sync = ['protocol_key', 'address', 'suid', 'sessid', 'uid', 'uname', 'logged_in', 'cid', 'encoding', - 'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total'] - + 'conn_time', 'cmd_last', 'cmd_last_visible', 'cmd_total', 'protocol_flags'] + def init_session(self, protocol_key, address, sessionhandler): """ Initialize the Session. This should be called by the protocol when @@ -71,6 +71,8 @@ class Session(object): self.cmd_last = self.conn_time self.cmd_total = 0 + self.protocol_flags = {} + # a back-reference to the relevant sessionhandler this # session is stored in. self.sessionhandler = sessionhandler diff --git a/src/server/telnet.py b/src/server/telnet.py index 6d1711f68b..7c910cecc5 100644 --- a/src/server/telnet.py +++ b/src/server/telnet.py @@ -7,17 +7,17 @@ sessions etc. """ -from twisted.conch.telnet import StatefulTelnetProtocol +from twisted.conch.telnet import Telnet, StatefulTelnetProtocol, IAC, LINEMODE from src.server.session import Session +from src.server import ttype from src.utils import utils, ansi -class TelnetProtocol(StatefulTelnetProtocol, Session): +class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): """ Each player connecting over telnet (ie using most traditional mud clients) gets a telnet protocol instance assigned to them. All communication between game and player goes through here. - """ - + """ def connectionMade(self): """ This is called when the connection is first @@ -26,9 +26,20 @@ class TelnetProtocol(StatefulTelnetProtocol, Session): # initialize the session client_address = self.transport.client self.init_session("telnet", client_address, self.factory.sessionhandler) + + # setup ttype + self.ttype = ttype.Ttype(self) + # add us to sessionhandler self.sessionhandler.connect(self) - + + def enableRemote(self, option): + """ + This sets up the options we allow for this protocol. + """ + return (option == LINEMODE or + option == ttype.TTYPE) + def connectionLost(self, reason): """ This is executed when the connection is lost for @@ -37,14 +48,34 @@ class TelnetProtocol(StatefulTelnetProtocol, Session): """ self.sessionhandler.disconnect(self) self.transport.loseConnection() - + + def dataReceived(self, data): + """ + This method will split the incoming data depending on if it + starts with IAC (a telnet command) or not. All other data will + be handled in line mode. + """ + # print "dataRcv:", data, + # try: + # for b in data: + # print ord(b), + # if b == chr(24): print "ttype found!" + # print "" + # except Exception, e: + # print str(e) + ":", str(data) + + if data and data[0] == IAC: + super(TelnetProtocol, self).dataReceived(data) + else: + StatefulTelnetProtocol.dataReceived(self, data) + def lineReceived(self, string): """ Telnet method called when data is coming in over the telnet connection. We pass it on to the game engine directly. """ self.sessionhandler.data_in(self, string) - + # Session hooks def disconnect(self, reason=None): diff --git a/src/server/ttype.py b/src/server/ttype.py new file mode 100644 index 0000000000..92ee8622b5 --- /dev/null +++ b/src/server/ttype.py @@ -0,0 +1,91 @@ +""" +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 +will return with information such as its name, if it supports colour +etc. If the client does not support TTYPE, this will be ignored. + +All data will be stored on the protocol's protocol_flags dictionary, +under the 'TTYPE' key. +""" + +# telnet option codes +TTYPE = chr(24) +IS = chr(0) +SEND = chr(1) + +# terminal capabilities and their codes +MTTS = [(128,'PROXY'), + (64, 'SCREEN READER'), + (32, 'OSC COLOR PALETTE'), + (16, 'MOUSE TRACKING'), + (8, '256 COLORS'), + (4, 'UTF-8'), + (2, 'VT100'), + (1, 'ANSI')] + +class Ttype(object): + """ + Handles ttype negotiations. Called and initiated by the + telnet protocol. + """ + def __init__(self, protocol): + """ + initialize ttype by storing protocol on ourselves and calling + the client to see if it supporst ttype. + + the ttype_step indicates how far in the data retrieval we've + gotten. + """ + self.ttype_step = 0 + self.protocol = protocol + self.protocol.protocol_flags['TTYPE'] = {} + + # setup protocol to handle ttype initialization and negotiation + self.protocol.negotiationMap[TTYPE] = self.negotiate_ttype + # ask if client will ttype, connect callback if it does. + self.protocol.will(TTYPE).addCallbacks(self.negotiate_ttype, self.no_ttype) + + def no_ttype(self, option): + """ + Callback if ttype is not supported by client. + """ + pass + + def negotiate_ttype(self, option): + """ + Handles negotiation of the ttype protocol once the + client has confirmed that it supports the ttype + protocol. + + The negotiation proceeds in several steps, each returning a + certain piece of information about the client. All data is + stored on protocol.protocol_flags under the TTYPE key. + """ + + self.ttype_step += 1 + + if self.ttype_step == 1: + # set up info storage and initialize subnegotiation + self.protocol.requestNegotiation(TTYPE, SEND) + else: + # receive data + option = "".join(option).lstrip(IS) + if self.ttype_step == 2: + self.protocol.protocol_flags['TTYPE']['CLIENTNAME'] = option + self.protocol.requestNegotiation(TTYPE, SEND) + elif self.ttype_step == 3: + self.protocol.protocol_flags['TTYPE']['TERM'] = option + self.protocol.requestNegotiation(TTYPE, SEND) + elif self.ttype_step == 4 and option.startswith('MTTS'): + option = int(option.strip('MTTS ')) + self.protocol.protocol_flags['TTYPE']['MTTS'] = option + for codenum, standard in MTTS: + if option == 0: + break + status = option % codenum < option + self.protocol.protocol_flags['TTYPE'][standard] = status + if status: + option = option % codenum + #print "ttype results:", self.protocol.protocol_flags['TTYPE'] + diff --git a/src/utils/create.py b/src/utils/create.py index 9793a27822..19087533d8 100644 --- a/src/utils/create.py +++ b/src/utils/create.py @@ -404,6 +404,7 @@ def create_player(name, email, password, email = "dummy@dummy.com" if user: new_user = user + email = user.email else: if is_superuser: new_user = User.objects.create_superuser(name, email, password) @@ -423,7 +424,6 @@ def create_player(name, email, password, if player_dbobj: new_db_player = player_dbobj else: - # create new database object new_db_player = PlayerDB(db_key=name, user=new_user) new_db_player.save() diff --git a/src/web/media/images/evennia_logo.png b/src/web/media/images/evennia_logo.png index 62942f7b1f..b636af6c06 100755 Binary files a/src/web/media/images/evennia_logo.png and b/src/web/media/images/evennia_logo.png differ