| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 | #!/usr/bin/env python3import sysimport refrom twisted.internet import deferfrom twisted.internet import protocolfrom twisted.internet import reactorfrom twisted.python import logfrom twisted.python.logfile import DailyLogFile# from twisted.enterprise import adbapiimport pendulumfrom subprocess import check_output# This isn't the best configuration, but it's simple# and works.  Mostly.try:    from config_dev import *except ModuleNotFoundError:    from config import *# Extract the version information from git.version = check_output(    [        "git",        "describe",        "--long",        "--tags",        #        "--dirty",        "--always",        "--match",        "v[0-9]\.[0-9]\.[0-9]",    ],    universal_newlines=True,).strip()# Cleans all ANSIcleaner = re.compile(r"\x1b\[[0-9;]*[A-Zmh]")# Looks for ANSI (that should be considered to be a newline)# This needs to see what is send when something enters / leaves# the player's current sector.  (That doesn't work/isn't# detected.  NNY!)makeNL = re.compile(r"\x1b\[[0-9;]*[J]")def treatAsNL(line):    """ Replace any ANSI codes that would be better understood as newlines. """    global makeNL    return makeNL.sub("\n", line)def cleanANSI(line):    """ Remove all ANSI codes. """    global cleaner    return cleaner.sub("", line)    # return re.sub(r'\x1b\[([0-9,A-Z]{1,2}(;[0-9]{1,2})?(;[0-9]{3})?)?[m|K]?', '', line)class Game(protocol.Protocol):    def __init__(self):        self.buffer = ""        self.game = ""    def connectionMade(self):        log.msg("Connected to Game Server")        self.queue_player = self.factory.queue_player        self.queue_game = self.factory.queue_game        self.setPlayerReceived()    def setPlayerReceived(self):        """ Get deferred from client queue, callback clientDataReceived. """        self.queue_player.get().addCallback(self.playerDataReceived)    def playerDataReceived(self, chunk):        if chunk is False:            self.queue_player = None            log.msg("Player: disconnected, close connection to game")            # I don't believe I need this if I'm using protocol.Factory            self.factory.continueTrying = False            self.transport.loseConnection()        else:            # Pass received data to the server            self.transport.write(chunk)            self.setPlayerReceived()    def lineReceived(self, line):        """ line received from the game. """        log.msg(">> [{0}]".format(line))        if "TWGS v2.20b" in line and "www.eisonline.com" in line:            # Must not be unicode            # Is there a way to NOT have this logged?            self.queue_game.put(                "TWGS Proxy build "                + version                + " is active. \x1b[1;34m~\x1b[0m to activate.\n\r\n\r",            )        if "TradeWars Game Server" in line and "Copyright (C) EIS" in line:            # We are not in a game            self.game = ""        if "Selection (? for menu): " in line:            game = line[-1]            if game >= "A" and game < "Q":                self.game = game                log.msg("Game: {0}".format(self.game))                self.game = game            #     self.setGame(game)    def dataReceived(self, chunk):        """ Data received from the Game.                 Remove backspaces.        Treat some ANSI codes as NewLine.                Remove ANSI.        Break into lines.        Trim out carriage returns.        Call lineReceived().                "Optionally" pass data to player.        FUTURE: trigger on prompt. [cleanANSI(buffer)]        """        # Sequence error:        # If I don't put the chunk(I received) to the player.        # anything I display -- lineReceive() put() ... would        # be out of order.  (I'd be responding -- before it        # was displayed to the user.)        # Possibly:  if self.passon ... send to player        self.queue_game.put(chunk)        self.buffer += chunk.decode("utf-8", "ignore")        # Process any backspaces        while "\x08" in self.buffer:            part = self.buffer.partition("\x08")            self.buffer = part[0][:-1] + part[2]        # Treat some ANSI codes as a newline        self.buffer = treatAsNL(self.buffer)        # Break into lines        while "\n" in self.buffer:            part = self.buffer.partition("\n")            line = part[0].replace("\r", "")            # Clean ANSI codes from line            line = cleanANSI(line)            self.lineReceived(line)            self.buffer = part[2]    def connectionLost(self, why):        log.msg("Game connectionLost because: %s" % why)        self.queue_game.put(False)        self.transport.loseConnection()class GlueFactory(protocol.ClientFactory):    # class GlueFactory(protocol.Factory):    maxDelay = 10    protocol = Game    def __init__(self, player):        self.player = player        self.queue_player = player.queue_player        self.queue_game = player.queue_game    def closeIt(self):        log.msg("closeIt")        self.queue_game.put(False)    def getUser(self, user):        log.msg("getUser( %s )" % user)        self.twgs.logUser(user)    # This was needed when I replaced ClientFactory with Factory.    # def clientConnectionLost(self, connector, why):    #     log.msg("clientconnectionlost: %s" % why)    #     self.queue_client.put(False)    def clientConnectionFailed(self, connector, why):        log.msg("connection to game failed: %s" % why)        self.queue_game.put(b"Sorry!  I'm Unable to connect to the game server.\r\n")        # syncterm gets cranky/locks up if we close this here.        # (Because it is still sending rlogin information?)        reactor.callLater(2, self.closeIt)class Player(protocol.Protocol):    def __init__(self):        self.buffer = ""        self.fpRaw = None        self.fpLines = None        self.action = None        self.username = ""        self.game = ""        self.passon = True    def connectionMade(self):        """ connected, setup queues.                 queue_player is data from player.        queue_game is data to player. (possibly from game)        """        self.queue_player = defer.DeferredQueue()        self.queue_game = defer.DeferredQueue()        self.setGameReceived()        # Connect GlueFactory to this Player object.        factory = GlueFactory(self)        # Make connection to the game server        reactor.connectTCP(HOST, PORT, factory, 5)    def setGameReceived(self):        """ Get deferred from client queue, callback clientDataReceived. """        self.queue_game.get().addCallback(self.gameDataReceived)    def gameDataReceived(self, chunk):        """ Data received from the client/player. """        if chunk is False:            self.transport.loseConnection()        else:            if type(chunk) == bytes:                self.transport.write(chunk)            elif type(chunk) == str:                self.transport.write(chunk.encode())            else:                log.err("gameDataReceived: type (%s) given!", type(chunk))                self.transport.write(chunk)            self.setGameReceived()    def dataReceived(self, chunk):        self.queue_player.put(chunk)    def connectionLost(self, why):        log.msg("lost connection %s" % why)        self.queue_player.put(False)    def connectionFailed(self, why):        log.msg("connectionFailed: %s" % why)if __name__ == "__main__":    if LOGFILE:        log.startLogging(DailyLogFile("proxy.log", "."))    else:        log.startLogging(sys.stdout)    log.msg("This is version: %s" % version)    factory = protocol.Factory()    factory.protocol = Player    reactor.listenTCP(LISTEN_PORT, factory, interface=LISTEN_ON)    reactor.run()
 |