Ejemplo n.º 1
0
    def onFirstPacket(self, packet):
        packetType = packet.uint8()
        IN_TEST = False

        if packetType == 0xFF:
            # Special case for tests.
            IN_TEST = True
            
        if packetType == 0x0A and not self.ready:
            packet.pos += 2 # OS 0x00 and 0x01
            #packet.uint16() 
            version = packet.uint16() # Version int
            
            if version >= 972:
                version = packet.uint32()
                packet.uint8() # Client type.

            self.protocol = game.protocol.getProtocol(version)
            self.version = version
            print "Client protocol version %d" % version

            if not self.protocol:
                log.msg("Trying to load a invalid protocol")
                self.transport.loseConnection()
                return

            if not IN_TEST:
                if (len(packet.data) - packet.pos) == 128: # RSA 1024 is always 128
                    packet.data = otcrypto.decryptRSA(packet.getData()) # NOTICE: Should we do it in a seperate thread?
                    packet.pos = 0 # Reset position

                else:
                    log.msg("RSA, length != 128 (it's %d)" % (packet.length - packet.pos))
                    self.transport.loseConnection()
                    return

                if not packet.data or packet.uint8(): # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0
                    log.msg("RSA, first char != 0")
                    self.transport.loseConnection()
                    return

                # Set the XTEA key
                k = (packet.uint32(), packet.uint32(), packet.uint32(), packet.uint32())
                sum = 0
                self.xtea = {}
                for x in xrange(32):
                    self.xtea[x] = sum + k[sum & 3] & 0xffffffff
                    sum = (sum + 0x9E3779B9) & 0xffffffff
                    self.xtea[32 + x] = sum + k[sum>>11 & 3] & 0xffffffff
                    

            ip = self.transport.getPeer().host
            
            # Ban check.
            if game.ban.ipIsBanned(ip):
                self.exitWithError("Your ip is banned.\n %s" % game.ban.banIps[ip].message())
                return 
        
            if config.gameMaxConnections <= (self.connections + len(waitingListIps)):
                if ip in waitingListIps:
                    i = waitingListIps.index(ip) + 1
                    lastChecks[ip] = time.time()
                    # Note: Everyone below this threshhold might connect. So even if your #1 on the list and there is two free slots, you can be unlucky and don't get them.
                    if i + self.connections > config.gameMaxConnections:
                        self.exitWaitingList("Too many players online. You are at place %d on the waiting list." % i, i) 
                        return
                else:
                    waitingListIps.append(ip)
                    lastChecks[ip] = time.time()
                    self.exitWaitingList("Too many players online. You are at place %d on the waiting list." % len(waitingListIps), len(waitingListIps)) 
                    return
            self.connections += 1
            try:
                waitingListIps.remove(ip)
                del lastChecks[ip]
            except:
                pass
            
            # "Gamemaster" mode?
            gamemaster = packet.uint8()

            # Check if version is correct
            if version > config.versionMax or version < config.versionMin:
                self.exitWithError(config.versionError)
                return

            # Some weird thing with 9.7.
            try:
                # Check if there is a username (and a password)
                username = packet.string()

                characterName = packet.string()

                password = packet.string()
            except:
                self.exitWithError("Try again.")
                return

            if (not username and not config.anyAccountWillDo) or not characterName:
                self.exitWithError("Could not get your account name, or character name")
                return

            packet.pos += 6 # I don't know what this is

            # Our funny way of doing async SQL
            account = yield sql.conn.runQuery("SELECT `id`,`language` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))", (username, password))

            if not account:
                account = game.scriptsystem.get("loginAccountFailed").runSync(None, client=self, username=username, password=password)
                if not account or account == True:
                    self.exitWithError("Invalid username or password")
                    return

            account = account[0]
            
            # Ban check.
            if game.ban.accountIsBanned(account['id']):
                self.exitWithError("Your account is banned.\n %s" % game.ban.banAccounts[account['id']].message())
                return 
            
            if not len(account) >= 2 or not account['language']:
                language = config.defaultLanguage
            else:
                language = account['language']
                
            character = yield sql.conn.runQuery("SELECT p.`id`,p.`name`,p.`world_id`,p.`group_id`,p.`account_id`,p.`vocation`,p.`health`,p.`mana`,p.`soul`,p.`manaspent`,p.`experience`,p.`posx`,p.`posy`,p.`posz`,p.`instanceId`,p.`sex`,p.`looktype`,p.`lookhead`,p.`lookbody`,p.`looklegs`,p.`lookfeet`,p.`lookaddons`,p.`lookmount`,p.`town_id`,p.`skull`,p.`stamina`, p.`storage`, p.`inventory`, p.`depot`, p.`conditions`, s.`fist`,s.`fist_tries`,s.`sword`,s.`sword_tries`,s.`club`,s.`club_tries`,s.`axe`,s.`axe_tries`,s.`distance`,s.`distance_tries`,s.`shield`,s.`shield_tries`,s.`fishing`, s.`fishing_tries`, g.`guild_id`, g.`guild_rank`, p.`balance` FROM `players` AS `p` LEFT JOIN player_skills AS `s` ON p.`id` = s.`player_id` LEFT JOIN player_guild AS `g` ON p.`id` = g.`player_id` WHERE p.account_id = %s AND p.`name` = %s AND p.`world_id` = %s", (account['id'], characterName, config.worldId))

            if not character:
                character = game.scriptsystem.get("loginCharacterFailed").runSync(None, client=self, account=account, name=characterName)
                if not character or character == True:
                    self.exitWithError("Character can't be loaded")
                    return
                
            character = character[0]
            if gamemaster and character['group_id'] < 3:
                self.exitWithError("You are not gamemaster! Turn off gamemaster mode in your IP changer.")
                return

            # Ban check.
            if isinstance(character, game.player.Player):
                if game.ban.playerIsBanned(character):
                    self.exitWithError("Your player is banned.\n %s" % game.ban.banAccounts[character.data["id"]].message())
                    return 
            elif game.ban.playerIsBanned(character['id']):
                self.exitWithError("Your player is banned.\n %s" % game.ban.banAccounts[character['id']].message())
                return 
            
            # If we "made" a new character in a script, character = the player.
            player = None
            if isinstance(character, game.player.Player):
                player = character
                game.player.allPlayers[player.name()] = player
            elif character['name'] in game.player.allPlayers:
                player = game.player.allPlayers[character['name']]
                if player.client:
                    self.exitWithError("This character is already logged in!")
                    return
                sql.runOperation("UPDATE `players` SET `lastlogin` = %s, `online` = 1 WHERE `id` = %s", (int(time.time()), character['id']))
            if player:    
                self.player = player
                if self.player.data["health"] <= 0:
                    self.player.onSpawn()
                self.player.client = self
                tile = getTile(self.player.position)
                tile.placeCreature(self.player)
                # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                updateTile(self.player.position, tile)
                
            else:
                # Bulld the dict since we disabled automaticly doing this. Here we cast Decimal objects to int aswell (no longer automaticly either)
                yield deathlist.loadDeathList(character['id'])
                character["language"] = language
                game.player.allPlayers[character['name']] = game.player.Player(self, character)
                self.player = game.player.allPlayers[character['name']]
                if self.player.data["health"] <= 0:
                    self.player.onSpawn()

                try:
                    tile = getTile(self.player.position)
                    tile.placeCreature(self.player)
                    # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                    updateTile(self.player.position, tile)
                        
                except AttributeError:
                    self.player.position = Position(*game.map.mapInfo.towns[1][1])
                    tile = getTile(self.player.position)
                    tile.placeCreature(self.player)
                    # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                    updateTile(self.player.position, tile)
                        
                # Update last login
                sql.runOperation("UPDATE `players` SET `lastlogin` = %s WHERE `id` = %s", (int(time.time()), character['id']))

            self.packet = self.player.packet
            self.player.sendFirstPacket()
            self.ready = True # We can now accept other packages

            # Call the login script
            game.scriptsystem.get("login").runSync(self.player)
            
            # If we got a waiting list, now is a good time to verify the list
            if lastChecks:
                checkTime = time.time()
                for entry in lastChecks:
                    if checkTime - lastChecks[entry] > 3600:
                        waitingListIps.remove(entry)
                        del lastChecks[entry]
                        
        elif packetType == 0x00 and self.transport.getPeer().host in config.executeProtocolIps:
            self.gotFirst = False
            t = TibiaPacket()
            if not config.executeProtocolAuthKeys:
                self.ready = 2
            try:
                while True:
                    op = packet.string()
                    print op
                    if op == "CALL" and self.ready == 2:
                        print "do this"
                        result = yield game.functions.executeCode(packet.string())
                        
                        t.string(result)
                    elif op == "AUTH":
                        print "auth"
                        result = packet.string() in config.executeProtocolAuthKeys
                        if result:
                            t.string("True")
                            self.ready = 2
                        else:
                            t.string("False")
            except struct.error:
                pass # End of the line
            t.send(self)
Ejemplo n.º 2
0
    def onFirstPacket(self, packet):
        global IPS
        try:
            packet.uint8()
        except:
            return


        if config.letGameServerRunTheLoginServer:
            pos = packet.pos
            packet.pos = 0
            packetType = packet.uint8()
            if packetType == 0xFF:
                if packetType == 0xFF:
                    if packet.getX(4) == "info":
                        try:
                            sendPlayers == packet.uint8()
                        except:
                            sendPlayers = False

                        data = ""
                        # TODO: Send XML format.

                elif packetType == 0x01:
                    # Silly status protocol. No multi world support...
                    reqInfo = packet.uint16()

                    pkg = TibiaPacket()

                    if reqInfo & 0x01: # REQUEST_BASIC_SERVER_INFO
                        pkg.uint8(0x10)
                        pkg.string(config.server[0][0])
                        pkg.string(config.server[0][1])
                        pkg.uint16(4)
                        pkg.uint32(config.loginPort)

                    if reqInfo & 0x02: # REQUEST_SERVER_OWNER_INFO
                        pkg.uint8(0x11)
                        pkg.string(config.ownerName)
                        pkg.string(config.ownerEmail)

                    if reqInfo & 0x04: # REQUEST_MISC_SERVER_INFO
                        pkg.uint8(0x12)
                        pkg.string(config.motd)
                        pkg.string(config.location)
                        pkg.string(config.url)
                        pkg.uint64(time.time() - SERVER_START + config.tibiaTimeOffset)

                    if reqInfo & 0x08: # REQUEST_PLAYERS_INFO
                        pkg.uint8(0x20)
                        pkg.uint32(len(core.game.allPlayers))
                        pkg.uint32(config.gameMaxConnections)
                        pkg.uint32(len(core.game.allPlayers)) # TODO: Track record.

                    if reqInfo & 0x10: # REQUEST_SERVER_MAP_INFO
                        pkg.uint8(0x30)
                        pkg.string(core.game.map.mapInfo.description)
                        pkg.string(core.game.map.mapInfo.author)
                        pkg.uint16(core.game.map.mapInfo.width)
                        pkg.uint16(core.game.map.mapInfo.height)

                    # 0x20 and 0x40 is unimplanted.

                    if reqInfo & 0x80:
                        pkg.uint8(0x23)
                        pkg.string("PyOT")
                        pkg.string(SERVER_VERSION)
                        pkg.string("%s-%s" % (config.versionMin, config.versionMax))

                    pkg.send(self)
                    return

            packet.pos = pos

        packet.pos += 2
        #packet.uint16() # OS 0x00 and 0x01
        version = packet.uint16() # Version int

        if version >= 971:
            version = packet.uint32()
            packet.uint8() # Client type.

        packet.pos += 12 # Checksum for files

        if (len(packet.data) - packet.pos) == 128: # RSA 1024 is always 128
            packet.data = otcrypto.decryptRSA(packet.getData())
            packet.pos = 0 # Reset position

        else:
            print("RSA, length != 128 (it's %d)" % (len(packet.data) - packet.pos))
            self.transport.loseConnection()
            return

        v = packet.uint8()
        if not len(packet.data) or v != 0: # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0
            print("RSA, first char != 0 ")
            self.transport.loseConnection()
            return
        # Set the XTEA key
        k = (packet.uint32(), packet.uint32(), packet.uint32(), packet.uint32())
        sum = 0
        self.xtea = [0] * 64
        for x in range(32):
            self.xtea[x] = sum + k[sum & 3] & 0xffffffff
            sum = (sum + 0x9E3779B9) & 0xffffffff
            self.xtea[32 + x] = sum + k[sum>>11 & 3] & 0xffffffff

        # If cffi, cast this.
        if otcrypto.ffi:
            self.xtea = otcrypto.ffi.new("const uint32_t[]", self.xtea)

        # Check if version is correct
        if version > config.versionMax or version < config.versionMin:
            self.exitWithError(config.versionError)
            print("Version incorrect")
            return


        # Check if there is a username (and a password)

        username = packet.string()
        password = packet.string()

        if not username and not config.anyAccountWillDo:
            self.exitWithError("You must enter your account number")
            return

        # Initialize the packet to send
        pkg = TibiaPacket()

        if username:
            # Our funny way of doing async SQL
            account = yield sql.runQuery("SELECT `id`, `premdays` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))", username, password)

            if account:
                characters = yield sql.runQuery("SELECT `name`,`world_id` FROM `players` WHERE account_id = %s", account[0]['id'])

        if not username or not account:
            if config.anyAccountWillDo:
                account = ((0,0),)
                characters = config.anyAccountPlayerMap
            else:
                self.exitWithError("Invalid username or password")
                return

        if config.letGameServerRunTheLoginServer:
            import game.scriptsystem
            game.scriptsystem.get("preSendLogin").run(client=self, characters=characters, account=account, username=username, password=password)

        # Send motd
        pkg.uint8(0x14)
        pkg.string(config.motd)

        # Add character list
        pkg.uint8(0x64)
        pkg.uint8(len(characters))
        for character in characters:
            ip = config.servers[character['world_id']][0]
            port = config.gamePort

            if ':' in ip:
                ip, port = ip.split(':')
                port = int(port)
            if self.address == '127.0.0.1':
                ip = '127.0.0.1'
            elif ip in IPS:
                ip = IPS[ip]
            elif ip != 'auto':
                _ip = ip
                ip = socket.gethostbyname(ip)
                IPS[_ip] = ip
            else:
                import urllib.request, urllib.error, urllib.parse
                try:
                    ip = urllib.request.urlopen("http://vapus.net/ip.php").read()
                except:
                    ip = ""

                if not ip:
                    raise Exception("[ERROR] Automatic IP service is down!")


                IPS['auto'] = ip
                # Save IPS here.
                pickle.dump(IPS, open('IP_CACHE', 'wb'), 2)

            pkg.string(character['name'])
            pkg.string(config.servers[character['world_id']][1])
            pkg.raw(socket.inet_aton(ip if type(ip) == str else ip.decode('utf-8')))
            pkg.length += 4
            pkg.uint16(port)

            if version >= 980:
                pkg.uint8(0)
        # Add premium days
        pkg.uint16(account[0]['premdays'])
        pkg.send(self) # Send
Ejemplo n.º 3
0
    def onFirstPacket(self, packet):
        packetType = packet.uint8()
        IN_TEST = False

        if packetType == 0xFF:
            # Special case for tests.
            IN_TEST = True

        if packetType == 0x0A and not self.ready:
            packet.pos += 2  # OS 0x00 and 0x01
            #packet.uint16()
            version = packet.uint16()  # Version int

            if version >= 972:
                version = packet.uint32()
                packet.uint8()  # Client type.

            self.protocol = game.protocol.getProtocol(version)
            self.version = version
            print "Client protocol version %d" % version

            if not self.protocol:
                log.msg("Trying to load a invalid protocol")
                self.transport.loseConnection()
                return

            if not IN_TEST:
                if (len(packet.data) -
                        packet.pos) == 128:  # RSA 1024 is always 128
                    packet.data = otcrypto.decryptRSA(packet.getData(
                    ))  # NOTICE: Should we do it in a seperate thread?
                    packet.pos = 0  # Reset position

                else:
                    log.msg("RSA, length != 128 (it's %d)" %
                            (packet.length - packet.pos))
                    self.transport.loseConnection()
                    return

                if not packet.data or packet.uint8(
                ):  # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0
                    log.msg("RSA, first char != 0")
                    self.transport.loseConnection()
                    return

                # Set the XTEA key
                k = (packet.uint32(), packet.uint32(), packet.uint32(),
                     packet.uint32())
                sum = 0
                self.xtea = {}
                for x in xrange(32):
                    self.xtea[x] = sum + k[sum & 3] & 0xffffffff
                    sum = (sum + 0x9E3779B9) & 0xffffffff
                    self.xtea[32 + x] = sum + k[sum >> 11 & 3] & 0xffffffff

            ip = self.transport.getPeer().host

            # Ban check.
            if game.ban.ipIsBanned(ip):
                self.exitWithError("Your ip is banned.\n %s" %
                                   game.ban.banIps[ip].message())
                return

            if config.gameMaxConnections <= (self.connections +
                                             len(waitingListIps)):
                if ip in waitingListIps:
                    i = waitingListIps.index(ip) + 1
                    lastChecks[ip] = time.time()
                    # Note: Everyone below this threshhold might connect. So even if your #1 on the list and there is two free slots, you can be unlucky and don't get them.
                    if i + self.connections > config.gameMaxConnections:
                        self.exitWaitingList(
                            "Too many players online. You are at place %d on the waiting list."
                            % i, i)
                        return
                else:
                    waitingListIps.append(ip)
                    lastChecks[ip] = time.time()
                    self.exitWaitingList(
                        "Too many players online. You are at place %d on the waiting list."
                        % len(waitingListIps), len(waitingListIps))
                    return
            self.connections += 1
            try:
                waitingListIps.remove(ip)
                del lastChecks[ip]
            except:
                pass

            # "Gamemaster" mode?
            gamemaster = packet.uint8()

            # Check if version is correct
            if version > config.versionMax or version < config.versionMin:
                self.exitWithError(config.versionError)
                return

            # Some weird thing with 9.7.
            try:
                # Check if there is a username (and a password)
                username = packet.string()

                characterName = packet.string()

                password = packet.string()
            except:
                self.exitWithError("Try again.")
                return

            if (not username
                    and not config.anyAccountWillDo) or not characterName:
                self.exitWithError(
                    "Could not get your account name, or character name")
                return

            packet.pos += 6  # I don't know what this is

            # Our funny way of doing async SQL
            account = yield sql.conn.runQuery(
                "SELECT `id`,`language` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))",
                (username, password))

            if not account:
                account = game.scriptsystem.get("loginAccountFailed").runSync(
                    None, client=self, username=username, password=password)
                if not account or account == True:
                    self.exitWithError("Invalid username or password")
                    return

            account = account[0]

            # Ban check.
            if game.ban.accountIsBanned(account['id']):
                self.exitWithError(
                    "Your account is banned.\n %s" %
                    game.ban.banAccounts[account['id']].message())
                return

            if not len(account) >= 2 or not account['language']:
                language = config.defaultLanguage
            else:
                language = account['language']

            character = yield sql.conn.runQuery(
                "SELECT p.`id`,p.`name`,p.`world_id`,p.`group_id`,p.`account_id`,p.`vocation`,p.`health`,p.`mana`,p.`soul`,p.`manaspent`,p.`experience`,p.`posx`,p.`posy`,p.`posz`,p.`instanceId`,p.`sex`,p.`looktype`,p.`lookhead`,p.`lookbody`,p.`looklegs`,p.`lookfeet`,p.`lookaddons`,p.`lookmount`,p.`town_id`,p.`skull`,p.`stamina`, p.`storage`, p.`inventory`, p.`depot`, p.`conditions`, s.`fist`,s.`fist_tries`,s.`sword`,s.`sword_tries`,s.`club`,s.`club_tries`,s.`axe`,s.`axe_tries`,s.`distance`,s.`distance_tries`,s.`shield`,s.`shield_tries`,s.`fishing`, s.`fishing_tries`, g.`guild_id`, g.`guild_rank`, p.`balance` FROM `players` AS `p` LEFT JOIN player_skills AS `s` ON p.`id` = s.`player_id` LEFT JOIN player_guild AS `g` ON p.`id` = g.`player_id` WHERE p.account_id = %s AND p.`name` = %s AND p.`world_id` = %s",
                (account['id'], characterName, config.worldId))

            if not character:
                character = game.scriptsystem.get(
                    "loginCharacterFailed").runSync(None,
                                                    client=self,
                                                    account=account,
                                                    name=characterName)
                if not character or character == True:
                    self.exitWithError("Character can't be loaded")
                    return

            character = character[0]
            if gamemaster and character['group_id'] < 3:
                self.exitWithError(
                    "You are not gamemaster! Turn off gamemaster mode in your IP changer."
                )
                return

            # Ban check.
            if isinstance(character, game.player.Player):
                if game.ban.playerIsBanned(character):
                    self.exitWithError(
                        "Your player is banned.\n %s" %
                        game.ban.banAccounts[character.data["id"]].message())
                    return
            elif game.ban.playerIsBanned(character['id']):
                self.exitWithError(
                    "Your player is banned.\n %s" %
                    game.ban.banAccounts[character['id']].message())
                return

            # If we "made" a new character in a script, character = the player.
            player = None
            if isinstance(character, game.player.Player):
                player = character
                game.player.allPlayers[player.name()] = player
            elif character['name'] in game.player.allPlayers:
                player = game.player.allPlayers[character['name']]
                if player.client:
                    self.exitWithError("This character is already logged in!")
                    return
                sql.runOperation(
                    "UPDATE `players` SET `lastlogin` = %s, `online` = 1 WHERE `id` = %s",
                    (int(time.time()), character['id']))
            if player:
                self.player = player
                if self.player.data["health"] <= 0:
                    self.player.onSpawn()
                self.player.client = self
                tile = getTile(self.player.position)
                tile.placeCreature(self.player)
                # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                updateTile(self.player.position, tile)

            else:
                # Bulld the dict since we disabled automaticly doing this. Here we cast Decimal objects to int aswell (no longer automaticly either)
                yield deathlist.loadDeathList(character['id'])
                character["language"] = language
                game.player.allPlayers[character['name']] = game.player.Player(
                    self, character)
                self.player = game.player.allPlayers[character['name']]
                if self.player.data["health"] <= 0:
                    self.player.onSpawn()

                try:
                    tile = getTile(self.player.position)
                    tile.placeCreature(self.player)
                    # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                    updateTile(self.player.position, tile)

                except AttributeError:
                    self.player.position = Position(
                        *game.map.mapInfo.towns[1][1])
                    tile = getTile(self.player.position)
                    tile.placeCreature(self.player)
                    # Send update tile to refresh all players. We use refresh because it fixes the order of things as well.
                    updateTile(self.player.position, tile)

                # Update last login
                sql.runOperation(
                    "UPDATE `players` SET `lastlogin` = %s WHERE `id` = %s",
                    (int(time.time()), character['id']))

            self.packet = self.player.packet
            self.player.sendFirstPacket()
            self.ready = True  # We can now accept other packages

            # Call the login script
            game.scriptsystem.get("login").runSync(self.player)

            # If we got a waiting list, now is a good time to verify the list
            if lastChecks:
                checkTime = time.time()
                for entry in lastChecks:
                    if checkTime - lastChecks[entry] > 3600:
                        waitingListIps.remove(entry)
                        del lastChecks[entry]

        elif packetType == 0x00 and self.transport.getPeer(
        ).host in config.executeProtocolIps:
            self.gotFirst = False
            t = TibiaPacket()
            if not config.executeProtocolAuthKeys:
                self.ready = 2
            try:
                while True:
                    op = packet.string()
                    print op
                    if op == "CALL" and self.ready == 2:
                        print "do this"
                        result = yield game.functions.executeCode(
                            packet.string())

                        t.string(result)
                    elif op == "AUTH":
                        print "auth"
                        result = packet.string(
                        ) in config.executeProtocolAuthKeys
                        if result:
                            t.string("True")
                            self.ready = 2
                        else:
                            t.string("False")
            except struct.error:
                pass  # End of the line
            t.send(self)
Ejemplo n.º 4
0
    def onFirstPacket(self, packet):
        global IPS
        try:
            packet.uint8()
        except:
            return

        
        if config.letGameServerRunTheLoginServer:
            pos = packet.pos
            packet.pos = 0
            packetType = packet.uint8()
            if packetType == 0xFF:
                if packetType == 0xFF:
                    if packet.getX(4) == "info":
                        try:
                            sendPlayers == packet.uint8()
                        except:
                            sendPlayers = False

                        data = ""
                        # TODO: Send XML format.
  
                elif packetType == 0x01:
                    # Silly status protocol. No multi world support...
                    reqInfo = packet.uint16()

                    pkg = TibiaPacket()

                    if reqInfo & 0x01: # REQUEST_BASIC_SERVER_INFO
                        pkg.uint8(0x10)
                        pkg.string(config.server[0][0])
                        pkg.string(config.server[0][1])
                        pkg.uint16(4)
                        pkg.uint32(config.loginPort)
   
                    if reqInfo & 0x02: # REQUEST_SERVER_OWNER_INFO
                        pkg.uint8(0x11)
                        pkg.string(config.ownerName)
                        pkg.string(config.ownerEmail)
                
                    if reqInfo & 0x04: # REQUEST_MISC_SERVER_INFO
                        pkg.uint8(0x12)
                        pkg.string(config.motd)
                        pkg.string(config.location)
                        pkg.string(config.url)
                        pkg.uint64(time.time() - SERVER_START + config.tibiaTimeOffset)

                    if reqInfo & 0x08: # REQUEST_PLAYERS_INFO
                        pkg.uint8(0x20)
                        pkg.uint32(len(core.game.allPlayers))
                        pkg.uint32(config.gameMaxConnections)
                        pkg.uint32(len(core.game.allPlayers)) # TODO: Track record.

                    if reqInfo & 0x10: # REQUEST_SERVER_MAP_INFO
                        pkg.uint8(0x30)
                        pkg.string(core.game.map.mapInfo.description)
                        pkg.string(core.game.map.mapInfo.author)
                        pkg.uint16(core.game.map.mapInfo.width)
                        pkg.uint16(core.game.map.mapInfo.height)

                    # 0x20 and 0x40 is unimplanted.

                    if reqInfo & 0x80:
                        pkg.uint8(0x23)
                        pkg.string("PyOT")
                        pkg.string(SERVER_VERSION)
                        pkg.string("%s-%s" % (config.versionMin, config.versionMax))

                    pkg.send(self)
                    return

            packet.pos = pos

        packet.pos += 2
        #packet.uint16() # OS 0x00 and 0x01
        version = packet.uint16() # Version int

        if version >= 971:
            version = packet.uint32()
            packet.uint8() # Client type.

        packet.pos += 12 # Checksum for files
        
        if (len(packet.data) - packet.pos) == 128: # RSA 1024 is always 128
            packet.data = otcrypto.decryptRSA(packet.getData()) # NOTICE: We don't have to yield this since we are already in a seperate thread?
            packet.pos = 0 # Reset position

        else:
            log.msg("RSA, length != 128 (it's %d)" % (len(packet.data) - packet.pos))
            self.transport.loseConnection()
            return

        if not packet.data or packet.uint8(): # RSA needs to decrypt just fine, so we get the data, and the first byte should be 0
            log.msg("RSA, first char != 0")
            self.transport.loseConnection()
            return

        # Set the XTEA key
        k = (packet.uint32(), packet.uint32(), packet.uint32(), packet.uint32())
        sum = 0
        self.xtea = {}
        for x in xrange(32):
            self.xtea[x] = sum + k[sum & 3] & 0xffffffff
            sum = (sum + 0x9E3779B9) & 0xffffffff
            self.xtea[32 + x] = sum + k[sum>>11 & 3] & 0xffffffff
                

        # Check if version is correct
        if version > config.versionMax or version < config.versionMin:
            self.exitWithError(config.versionError)
            return

        # Check if there is a username (and a password)
        username = packet.string()
        password = packet.string()

        if not username and not config.anyAccountWillDo:
            self.exitWithError("You must enter your account number")
            return

        # Initialize the packet to send
        pkg = TibiaPacket()

        if username:
            # Our funny way of doing async SQL
            account = yield sql.conn.runQuery("SELECT `id`, `premdays` FROM `accounts` WHERE `name` = %s AND `password` = SHA1(CONCAT(`salt`, %s))", (username, password))

            if account:
                characters = yield sql.conn.runQuery("SELECT `name`,`world_id` FROM `players` WHERE account_id = %s", (account[0]['id']))
     
        if not username or not account:
            if config.anyAccountWillDo:
                account = ((0,0),)
                characters = config.anyAccountPlayerMap
            else:
                self.exitWithError("Invalid username or password")
                return 
        
        if config.letGameServerRunTheLoginServer:
            import game.scriptsystem
            game.scriptsystem.get("preSendLogin").runSync(None, client=self, characters=characters, account=account, username=username, password=password)

        # Send motd
        pkg.uint8(0x14)
        pkg.string(config.motd)

        # Add character list
        pkg.uint8(0x64)
        pkg.uint8(len(characters))
        for character in characters:
            ip = config.servers[character['world_id']][0]
            port = config.gamePort

            if ':' in ip:
                ip, port = ip.split(':')
                port = int(port)
            if self.transport.getPeer().host == '127.0.0.1':
                ip = '127.0.0.1'
            elif ip in IPS:
                ip = IPS[ip]
            elif ip != 'auto':
                _ip = ip
                ip = socket.gethostbyname(ip)
                IPS[_ip] = ip
            else:
                import urllib2
                try:
                    ip = urllib2.urlopen("http://vapus.net/ip.php").read()
                except:
                    ip = ""

                if not ip:
                    raise Exception("[ERROR] Automatic IP service is down!")

                    
                IPS['auto'] = ip
                # Save IPS here.
                cPickle.dump(IPS, open('IP_CACHE', 'wb'), 2)

            pkg.string(character['name'])
            pkg.string(config.servers[character['world_id']][1])
            pkg.raw(socket.inet_aton(ip))
            pkg.uint16(port)
            
            if version >= 980:
                pkg.uint8(0)
        # Add premium days
        pkg.uint16(account[0]['premdays'])
        pkg.send(self) # Send