예제 #1
0
    def _handle_packet(self, packet):
        """ Find and call a handler for that packet.

        It is possible that we do not know the opcode, which is not a problem.

        If it is known, it should be legal (in a valid state) and we need to
        enforce a handler to it, even if it's just NopHandler. If I don't feel
        like adding the simplest handler for an opcode, it probably shouldn't be
        in the OpCode enum in the first place. If an opcode is not valid, we do
        not throw an error anymore because there are some cases where misc
        opcodes can be received just after changing states.
        """
        opcode, packet_data = self._parse_packet(packet)
        if opcode is None:
            return

        if (self.state not in self.UNMANAGED_STATES
                and opcode not in self.UNMANAGED_OPS
                and not self.opcode_is_legal(opcode)):
            LOG.debug("{}: received illegal opcode {} in state {}".format(
                type(self).__name__, opcode.name, self.state.name))
            return

        handler_class = self.OP_HANDLERS.get(opcode, self.DEFAULT_HANDLER)
        self._call_handler(handler_class, packet_data)
예제 #2
0
    def create_char(char_values):
        """ Try to create a new character and add it to the database. Return 0
        on success, 1 on unspecified failure, 2 on name already used, 3 if the
        race and class combination isn't supported.

        The arg char_values is a tuple containing the Character data in the
        order they're defined, from name to features. This last value has to be
        a tuple with CharacterFeatures fields values.

        This should check of other things like account char limit etc.
        """
        consts = _CharacterCreator._get_constants(char_values)
        if consts is None:
            return 3

        if CharacterManager.does_char_with_name_exist(char_values["name"]):
            return 2

        char_data = _CharacterCreator._try_create_char(char_values, consts)
        if char_data is None:
            return 1

        _CharacterCreator._add_default_skills(char_data, consts)
        _CharacterCreator._add_default_spells(char_data, consts)

        LOG.debug("Character " + char_data.name + " created.")
        return 0
예제 #3
0
 def _accept_clients(self):
     """ Accept incoming clients connections until manual interruption. """
     try:
         while not self.shutdown_flag.is_set():
             self._try_accept_client()
     except KeyboardInterrupt:
         LOG.info("KeyboardInterrupt received, stop accepting clients.")
예제 #4
0
 def delete_session(account):
     """ Delete the session assiociated with that account. """
     try:
         session = AccountSession.get(AccountSession.account == account)
         session.delete_instance()
     except AccountSession.DoesNotExist:
         LOG.warning("Tried to delete an non-existing session.")
예제 #5
0
 def _accept_clients(self):
     """ Regularly try to access client while looking for interrupts. """
     try:
         while True:
             self._try_accept_client()
     except KeyboardInterrupt:
         LOG.info("KeyboardInterrupt received, stop accepting clients.")
예제 #6
0
 def get_account(account_name):
     """ Return the account from the database if it exists, or None. """
     try:
         return Account.get(Account.name == account_name)
     except Account.DoesNotExist:
         LOG.warning("No account with that name: " + account_name)
         return None
예제 #7
0
    def process(self):
        guid = self.PACKET_BIN.unpack(self.packet)[0]
        character_data = self._get_checked_character(guid)
        if character_data is None:
            LOG.warning(
                "Account {} tried to illegally use character {}".format(
                    self.conn.account.name, guid))
            return self.conn.MAIN_ERROR_STATE, None

        # Now that we have the character data, spawn a new player object.
        self.conn.set_player(character_data)

        # Finally, send the packets necessary to let the client get in world.
        # Only the tutorial flags and update object packets are really necessary
        # to let the client show the world.
        self.conn.send_packet(self._get_verify_login_packet())
        self.conn.send_packet(self._get_account_data_md5_packet())
        self.conn.send_packet(self._get_tutorial_flags_packet())
        self.conn.send_packet(self._get_update_object_packet())
        # self.conn.send_packet(self._get_initial_spells_packet())

        self._get_near_objects()
        self._notify_near_players()

        return WorldConnectionState.IN_WORLD, None
예제 #8
0
 def _recv_packet(self):
     try:
         packet = self.world_packet_receiver.get_next_packet()
         return packet
     except ConnectionResetError:
         LOG.info("Lost connection with " + self.account.name + ".")
         return None
예제 #9
0
 def _add_required_fields(self, player):
     for required_field in PLAYER_SPAWN_FIELDS:
         value = player.get(required_field)
         if value is None:
             LOG.error("A required field for player spawning is not set.")
             LOG.error(str(required_field))
             continue
         self.add_field(required_field, value)
예제 #10
0
    def _actions_after_main_loop(self):
        LOG.debug("WorldConnection: session ended.")
        if self.account and self.session_cipher:
            AccountSessionManager.delete_session(self.account)
        if self.player:
            self.unset_player()

        with self.server.world_connections_lock:
            self.server.world_connections.remove(self)
예제 #11
0
    def remove_player(self, guid):
        """ Remove the player from the object list and save its data. """
        player = self.get_player(guid)
        if player is None:
            LOG.warning("Tried to remove a non-existing player.")
            return

        self._remove_object(guid)
        self.save_player(player)
예제 #12
0
    def _get_response_packet(self, manager_code):
        response_code = {
            0: CharDeleteResponseCode.SUCCESS,
            1: CharDeleteResponseCode.FAILED
        }.get(manager_code, 1)
        LOG.debug("Character deletion status: " + str(response_code.name))

        response_data = self.RESPONSE_BIN.pack(response_code.value)
        return WorldPacket(OpCode.SMSG_CHAR_DELETE, response_data)
예제 #13
0
 def _try_handle_packet(self, packet):
     try:
         self._handle_packet(packet)
     except Exception as exc:
         LOG.error("{}: uncaught exception in packet handler:".format(
             type(self).__name__))
         LOG.error(str(exc))
         traceback.print_tb(exc.__traceback__)
         self.state = self.MAIN_ERROR_STATE
예제 #14
0
    def _handle_client(self, connection, address):
        """ Start the threaded WorldConnection and add it to the local list. """
        address_string = str(address[0]) + ":" + str(address[1])
        LOG.info("Accepting client connection from " + address_string)
        world_connection = WorldConnection(self, connection)

        with self.world_connections_lock:
            self.world_connections.append(world_connection)

        simple_thread(world_connection.handle_connection)
예제 #15
0
    def start(self):
        LOG.info("Starting world server " + self.realm.name)
        self._listen_clients()

        simple_thread(self._handle_login_server_connection)
        self._accept_clients()

        self.shutdown_flag.set()
        self._stop_listen_clients()
        LOG.info("World server stopped.")
예제 #16
0
 def process(self):
     if "worldport_ack_pending" in self.conn.shared_data:
         LOG.debug("Received expected " +
                   str(OpCode.MSG_MOVE_WORLDPORT_ACK))
         del self.conn.shared_data["worldport_ack_pending"]
         return None, None
     else:
         LOG.error("Received unexpected " +
                   str(OpCode.MSG_MOVE_WORLDPORT_ACK))
         return self.conn.MAIN_ERROR_STATE, None
예제 #17
0
    def _slice_packet_opcode(self):
        """ Cut the packet opcode from content. """
        opcode_bytes = self.content[:4]
        opcode_value = int.from_bytes(opcode_bytes, "little")
        self.content = self.content[4:]

        try:
            self.opcode = OpCode(opcode_value)
        except ValueError:
            LOG.warning("Unknown opcode {:X}".format(opcode_value))
예제 #18
0
    def _get_response_packet(self, manager_code):
        response_code = {
            0: CharCreateResponseCode.SUCCESS,
            1: CharCreateResponseCode.FAILED,
            2: CharCreateResponseCode.NAME_IN_USE,
            3: CharCreateResponseCode.ERROR
        }.get(manager_code, 1)
        LOG.debug("Character creation status: " + str(response_code.name))

        response_data = self.RESPONSE_BIN.pack(response_code.value)
        return WorldPacket(OpCode.SMSG_CHAR_CREATE, response_data)
예제 #19
0
 def _try_create_char(char_values, consts):
     char_data = None
     with DB.atomic() as transaction:
         try:
             char_data = _CharacterCreator._create_char(char_values, consts)
         except PeeweeException as exc:
             LOG.error("An error occured while creating character:")
             LOG.error(str(exc))
             transaction.rollback()
             return None
     return char_data
예제 #20
0
 def _open_login_server_socket(self):
     """ Open the login server socket, or set it to None if it couldn't
     connect properly. """
     self.login_server_socket = socket.socket()
     login_server_address = (CONFIG["login"]["realm_conn_hostname"],
                             int(CONFIG["login"]["realm_conn_port"]))
     try:
         self.login_server_socket.connect(login_server_address)
     except ConnectionError as exc:
         LOG.error("Couldn't join login server! " + str(exc))
         self.login_server_socket = None
예제 #21
0
 def process(self):
     self._parse_packet(self.packet)
     self._generate_local_proof()
     if self.client_proof == self.local_proof:
         LOG.debug("Reconnection: correct proof")
         response = self._get_success_response()
         return LoginConnectionState.RECON_PROOF, response
     else:
         LOG.warning("Reconnection: wrong proof!")
         response = self._get_failure_response()
         return LoginConnectionState.CLOSED, response
예제 #22
0
 def _maintain_realm_list(self):
     """ Maintain realmlist by removing realms not updated for a while. """
     with self.locks["realms"]:
         to_remove = []
         for realm in self.realms:
             update_delay = time.time() - self.realms[realm]["last_update"]
             if update_delay > self.REALM_MAX_UPDATE_TIME:
                 to_remove.append(realm)
                 LOG.debug("Realm " + realm + " down, removed from list.")
         for realm_to_remove in to_remove:
             del self.realms[realm_to_remove]
예제 #23
0
    def start(self):
        LOG.info("Starting login server")
        self._start_listen()

        simple_thread(self._accept_realms)
        self._accept_clients()

        self.shutdown_flag.set()
        self._stop_listen()
        AccountSessionManager.delete_all_sessions()
        LOG.info("Login server stopped.")
예제 #24
0
    def _generate_local_proof(self):
        account_name = self.conn.account.name
        session = AccountSessionManager.get_session(account_name)
        if session is None:
            LOG.warning("Reconnection proof: account wasn't logged in!")
            return

        challenge = self.conn.recon_challenge
        to_hash = (account_name.encode("ascii") + self.proof_data + challenge +
                   session.session_key_as_bytes)
        self.local_proof = sha1(to_hash)
예제 #25
0
    def _try_recv_packet(self):
        packet, has_timeout = None, False

        try:
            packet = self._recv_packet()
            if packet is None:
                LOG.debug("Client closed the connection.")
        except socket.timeout:
            has_timeout = True

        return packet, has_timeout
예제 #26
0
    def _get_more_data(self):
        some_data = None
        try:
            some_data = self.socket.recv(1024)
        except ConnectionError as exc:
            LOG.warning("WorldPacketReceiver: ConnectionError: " + str(exc))
            traceback.print_tb(exc.__traceback__)

        if not some_data:
            raise WorldPacketReceiverException()

        self.packet_buf += some_data
예제 #27
0
 def _recv_packet(self):
     # This assumes that all packets are received in no more or less than one
     # piece, which is a wrong. However, exceptions shouldn't occur often
     # with how short login messages are.
     try:
         data = self.socket.recv(1024)
         if data and DEBUG:
             print(get_data_dump(data), end="")
         return data or None
     except ConnectionError:
         LOG.info("Lost connection.")
         return None
예제 #28
0
 def _process_reconnection(self):
     session = AccountSessionManager.get_session(self.account_name)
     if session is not None:
         LOG.debug("Reconnection: account was logged in.")
         self.conn.account = ReconChallenge._get_session_account(session)
         self.conn.recon_challenge = os.urandom(16)
         response = self._get_success_response()
         return LoginConnectionState.RECON_CHALL, response
     else:
         LOG.warning("Reconnection: account wasn't logged in!")
         response = self._get_failure_response()
         return LoginConnectionState.CLOSED, response
예제 #29
0
 def delete_char(guid):
     """ Try to delete character and all associated data from the database.
     Return 0 on success, 1 on error. """
     with DB.atomic() as transaction:
         try:
             _CharacterDestructor._delete_char(guid)
         except PeeweeException as exc:
             LOG.error("An error occured while deleting character:")
             LOG.error(str(exc))
             transaction.rollback()
             return 1
     return 0
예제 #30
0
    def process(self):
        self._parse_packet(self.packet)
        LOG.debug("NameQuery: GUID {:X}".format(self.guid))

        object_manager = self.conn.server.object_manager
        unit = object_manager.get_player(self.guid)
        if unit is None:
            LOG.warning("NameQueryHandler: couldn't find player {:X}".format(
                self.guid))
            return None, None

        response = self._get_response_packet(unit)
        return None, response