Пример #1
0
    async def handle_connection(self, reader: StreamReader, writer: StreamWriter):
        temp_ref = TempRef()
        world_packet_manager = WorldPacketManager(temp_ref=temp_ref, reader=reader, writer=writer)

        peername = writer.get_extra_info('peername')
        Logger.debug('[World Server]: Accept connection from {}'.format(peername))

        Logger.info('[World Server]: trying to process auth session')
        auth = AuthManager(reader, writer, temp_ref=temp_ref, world_packet_manager=world_packet_manager)
        await auth.process(step=AuthStep.SECOND)

        self._register_tasks()

        while True:
            try:
                request = await asyncio.wait_for(reader.read(4096), timeout=1.0)
                if request:
                    response = await asyncio.wait_for(world_packet_manager.process(request), timeout=1.0)

                    if response:
                        for packet in response:
                            writer.write(packet)
                            await writer.drain()

            except TimeoutError:
                continue

            except Exception as e:
                Logger.error('[World Server]: exception, {}'.format(e))
                traceback.print_exc()
                break

        writer.close()
Пример #2
0
    async def process(self):
        Logger.debug('[Login Challenge]: processing')
        self._parse_data()

        try:
            with AccountManager() as account_mgr:
                account = account_mgr.get(name=self.account_name).account

                if account is None:
                    raise Exception('Account \'{}\' is not found'.format(self.account_name))

                account.os = self.os
                account.ip = '.'.join([str(i) for i in self.ip_addr])
                account.platform = self.platform
                account.timezone = self.timezone
                account.locale = self.locale

                account_mgr.update()

                self.account = account
                self.temp_ref.account = account

        # TODO: define account exceptions
        except Exception as e:
            Logger.error('[Login Challenge]: error = {}'.format(e))
            return None
        finally:
            return self._get_response()
Пример #3
0
    async def authenticate_on_login_server(self):
        while True:
            try:
                request = await asyncio.wait_for(self.reader.read(1024),
                                                 timeout=0.01)
                if request:
                    opcode, packet = request[0], request[1:]
                    try:
                        handler = AuthManager.AUTH_HANDLERS[LoginOpCode(
                            opcode)]
                    except ValueError:
                        Logger.error(
                            '[AuthManager]: Incorrect request, check the opcode'
                        )
                        pass
                    else:
                        response = await handler(
                            packet=packet,
                            srp=self.srp,
                            temp_ref=self.temp_ref).process()

                        if response:
                            self.writer.write(response)
            except TimeoutError:
                pass
            finally:
                await asyncio.sleep(0.01)
Пример #4
0
 def _get_response(self):
     try:
         response = pack('<2B20sQ2B', LoginOpCode.LOGIN_PROOF.value,
                         LoginResult.SUCCESS.value, self.srp.server_proof,
                         0x00800000, 0x00, 0x00)
     except Exception as e:
         Logger.error('[Login Proof]: {}'.format(e))
     else:
         return response
Пример #5
0
    async def process(self, packet: bytes):
        if not self.header_crypt:
            raise Exception('Cannot decrypt packet')

        # this is workaround cause one-time decryption do not works correctly for some opcodes
        # so I need decrypt some packets for multiple times
        def decrypt(packet: bytes):
            result = packet
            for index in range(20):
                enc = self.header_crypt.decrypt(packet)
                try:
                    # TODO: add has_key for Enum
                    WorldOpCode(int.from_bytes(enc[2:6], 'little')).value
                except ValueError:
                    continue
                else:
                    result = enc
                    break

            return result

        packet = decrypt(packet)

        size = unpack('>H', packet[:2])[0]
        opcode = WorldOpCode(unpack('<I', packet[2:6])[0])

        if opcode in HANDLERS:
            Logger.debug(
                '[World Packet]: processing {} opcode ({} bytes)'.format(
                    WorldOpCode(opcode).name, size))
            handlers = HANDLERS[opcode]
            packets = list()

            for handler in handlers:
                opcode, response = await handler(
                    packet,
                    temp_ref=self.temp_ref,
                    reader=self.reader,
                    writer=self.writer,
                    header_crypt=self.header_crypt).process()
                if opcode and response:
                    packets.append(
                        WorldPacketManager.generate_packet(
                            opcode, response, self.header_crypt))

            return packets
        else:
            try:
                Logger.warning(
                    '[World Packet]: no handler for opcode = {} ({} bytes)'.
                    format(WorldOpCode(opcode).name, size))
            except ValueError:
                Logger.error(
                    '[World Packet]: no handler for unknown opcode = {} ({} bytes)'
                    .format(opcode, size))
            finally:
                return None
Пример #6
0
 def _parse_data(self):
     try:
         parsed_data = unpack(LoginProof.LOGIN_PROOF_FORMAT, self.packet)
         self.client_ephemeral = int.from_bytes(parsed_data[0], 'little')
         self.client_proof = parsed_data[1]
         self.checksum = parsed_data[2]
         self.unk = parsed_data[3]
     except StructError as e:
         Logger.error('[Login Proof]: on unpacking data(len={}), error={}'.format(len(self.packet), StructError))
Пример #7
0
    def delete(self, **kwargs):
        if kwargs:
            try:
                self.session.query(Account).filter_by(**kwargs).delete()
            except Exception as e:
                Logger.error(
                    '[AccountManager]: Error has occured on account delete, {}'
                    .format(e))

        return self
Пример #8
0
    def set_default_equipment(self):
        self.session.expunge(self.player.region)
        self.session.expunge(self.player.account)
        self.session.expunge(self.player)

        with EquipmentManager() as equipment_mgr:
            try:
                self.equipment = equipment_mgr.set_default_equipment(
                    player=self.player).get_items()
            except Exception as e:
                Logger.error('[PlayerMgr]: equipment error = {}'.format(e))
Пример #9
0
    def get(self, **kwargs):
        if kwargs:
            try:
                self.account = self.session.query(Account).filter_by(
                    **kwargs).first()
            except Exception as e:
                Logger.error(
                    '[AccountManager]: Error has occured, account will be None, error: {}'
                    .format(e))

        return self
Пример #10
0
    async def handle_connection(self, reader: StreamReader,
                                writer: StreamWriter):
        self._register_tasks()

        temp_ref = TempRef()
        world_packet_manager = WorldPacketManager(temp_ref=temp_ref,
                                                  reader=reader,
                                                  writer=writer)

        Logger.info('[World Server]: trying to process auth session')
        auth = AuthManager(reader,
                           writer,
                           temp_ref=temp_ref,
                           world_packet_manager=world_packet_manager,
                           session_keys=self.session_keys)

        is_authenticated = await auth.process(step=AuthStep.SECOND)

        if is_authenticated:

            peer_name = writer.get_extra_info('peername')
            Logger.success(
                '[World Server]: Accept connection from {}'.format(peer_name))

            while True:
                try:
                    request = await asyncio.wait_for(reader.read(4096),
                                                     timeout=0.01)
                    if request:
                        response = await asyncio.wait_for(
                            world_packet_manager.process(request),
                            timeout=0.01)

                        if response:
                            for packet in response:
                                writer.write(packet)
                                await writer.drain()

                except TimeoutError:
                    pass

                except BrokenPipeError:
                    pass

                except Exception as e:
                    Logger.error('[World Server]: exception, {}'.format(e))
                    traceback.print_exc()
                    break
                finally:
                    await asyncio.sleep(0.01)

        writer.close()
Пример #11
0
    async def process(self):
        self._load_player()

        if not self.temp_ref.player:
            Logger.error('[Player Init]: player not exists')
            raise PlayerNotExists

        await QueuesRegistry.connections_queue.put(
            (self.temp_ref.player.name, self.reader, self.writer,
             self.header_crypt))
        await QueuesRegistry.players_queue.put(self.temp_ref.player)

        return None, None
Пример #12
0
    def _check_session_key(self):
        Logger.info('[Auth Session Manager]: checking session key')
        key = '#{}-session-key'.format(self.account_name)

        try:
            session_key = self.session_keys[key]
        except KeyError:
            Logger.error('[AuthMgr]: session with this key does not exists')
            self.writer.close()
        else:
            if not session_key:
                raise Exception('[AuthMgr]: Session key does not exists')

            self.session_key = b64decode(session_key)
Пример #13
0
    def _get_verify_login_packet(self):
        player = self.temp_ref.player

        if not player:
            Logger.error('[Login Verify]: player not exists')
            return None

        return pack(
            '<I4f',
            player.map_id,            # map id
            player.x,                 # x
            player.y,                 # y
            player.z,                 # z
            player.orientation        # orientation
        )
Пример #14
0
    def load(self):
        current_dir = path.dirname(__file__)
        main_config = 'Config.yml'

        with open(path.join(current_dir, main_config), 'r') as stream:
            try:
                data = yaml.load(stream, Loader=yaml.Loader)
                self.config = json.loads(
                    json.dumps(data),
                    object_hook=lambda d: namedtuple('Configs', d.keys())
                    (*d.values()))
            except yaml.YAMLError as error:
                Logger.error('[Config Manager]: {}'.format(error))
            else:
                return self
Пример #15
0
    async def process(self):
        test_item1 = session.player.equipment.get_object_field(CharacterEquipSlot.BODY)
        test_item2 = session.player.equipment.get_object_field(CharacterEquipSlot.CHEST)
        test_item3 = session.player.equipment.get_object_field(CharacterEquipSlot.LEGS)
        test_item4 = session.player.equipment.get_object_field(CharacterEquipSlot.FEET)

        spawned_item = UpdatePacket(test_item1, ObjectUpdateType.CREATE_OBJECT.value, False)

        for field in self.SPAWN_FIELDS:
            spawned_item.add_field(field, test_item1.get_object_field(field))

        response = spawned_item.update(send_packed_guid=True)

        Logger.error('[ItemSpawn]: {}'.format(response))

        return WorldOpCode.SMSG_UPDATE_OBJECT, response
Пример #16
0
    async def refresh_creatures(self):
        for region in self.regions:
            try:
                region_units = region.units.copy()
            except DetachedInstanceError as e:
                Logger.error('[Region Manager]: {}'.format(e))
            else:
                units = region_units.copy()

                # finally building packet for player that contains unit list
                movement_flags = (
                    UpdateObjectFlags.UPDATEFLAG_HIGHGUID.value
                    | UpdateObjectFlags.UPDATEFLAG_LIVING.value
                    | UpdateObjectFlags.UPDATEFLAG_HAS_POSITION.value)

                spawn_dist = Config.World.Gameplay.spawn_dist

                online_players = region.get_online_players()

                if not spawn_dist == 0:
                    for player_name in online_players:
                        player = online_players[player_name]

                        # list of unit managers each ready to build the update packet
                        update_packets = []

                        if spawn_dist > 0:
                            units = [
                                unit for unit in units
                                if RegionManager._is_unit_in_spawn_radius(
                                    unit, player)
                            ]

                        for unit in units:
                            with UnitManager() as unit_mgr:
                                unit_mgr.set(unit)
                                unit_mgr.movement.set_update_flags(
                                    movement_flags)

                                batch_builder = unit_mgr.prepare() \
                                    .build_update_packet(RegionManager.UNIT_SPAWN_FIELDS)

                                update_packets.append(batch_builder)

                        asyncio.ensure_future(
                            QueuesRegistry.update_packets_queue.put(
                                (player.name, update_packets)))
Пример #17
0
    async def authenticate_on_world_server(self):
        self.send_auth_challenge()
        try:
            await self._parse_data()
            await self._check_session_key()
            self._generate_server_hash()
            # after this step next packets will be encrypted
            self._setup_encryption()

            if self.server_hash != self.client_hash:
                raise Exception('[Auth Manager]: Server hash is differs from client hash')
            else:
                self._send_addon_info()
                self._send_auth_response()

        except TimeoutError:
            Logger.error('[Auth Manager]: Timeout on step2')
Пример #18
0
    def _parse_account_name(self, buffer: BytesIO):
        Logger.info('[Auth Session Manager]: parsing account name')
        result = bytes()

        while True:
            char = buffer.read(1)
            if char and char != b'\x00':
                result += char
            else:
                break

        try:
            result = result.decode('utf-8')
        except UnicodeDecodeError:
            Logger.error('[Auth Session Manager]: decode error, wrong name = {}'.format(result))
        else:
            return result
Пример #19
0
    async def handle_connection(self, websocket: WebSocketCommonProtocol, path):
        self._register_tasks(websocket=websocket)

        while True:
            try:
                response = {'type': 'REGIONS_FETCH_LIST_RESPONSE', 'payload': self.web_data}
                await websocket.send(json.dumps(response))
            except ConnectionClosedOK:
                Logger.error('[Websocket Server]: Connection was closed')
                return
            except Exception as e:
                Logger.error('[Websocket Server]: {}'.format(e))
                Logger.warning(self.web_data)
                traceback.print_exc()
                continue
            finally:
                await asyncio.sleep(1)
Пример #20
0
    def add(self, field, value, offset=0):
        try:
            field_type = FIELD_TYPE_MAP[field]
        except KeyError:
            Logger.error(
                '[UpdatePacket Block Builder]: no type associated with {}'.
                format(str(field)))
            return
        else:
            field_struct = self.FIELD_BIN_MAP[field_type]
            index = offset and (field.value + offset) or field
            field_index = UpdateBlocksBuilder._get_field_index(index)

            self._set_field_mask_bits(field_index, field_struct)
            try:
                self._set_field_value(field_index, field_struct, value)
            except StructError:
                Logger.warning(
                    'Field with index {} should be set'.format(field_index))
                pass
Пример #21
0
    def to_json(self):
        exclude_keys = ['_sa_instance_state']

        # TODO: I think this can be refactored in the future,
        # this check need to avoid RecursionError
        if type(self).__name__ in ['Unit', 'Player', 'Item']:
            exclude_keys += ['region']

        result = {}
        try:
            for key in self.__dict__:
                if key not in exclude_keys:
                    if isinstance(self.__dict__[key], InstrumentedList):
                        result[key] = [
                            obj.to_json() for obj in self.__dict__[key]
                        ]
                    elif isinstance(self.__dict__[key], BaseModel):
                        result[key] = self.__dict__[key].to_json()
                    elif isinstance(self.__dict__[key], dict):
                        sub_result = {}
                        for _key in self.__dict__[key]:
                            item = self.__dict__[key][_key]
                            if isinstance(item, BaseModel):
                                sub_result[_key] = item.to_json()
                            else:
                                sub_result[_key] = item
                        result[key] = sub_result
                    elif isinstance(self.__dict__[key], (bytes, bytearray)):
                        pass
                    else:
                        result[key] = self.__dict__[key]
        except AttributeError as e:
            Logger.error('[BaseModel][Player]: {}'.format(e))
            raise e
        except RecursionError as e:
            Logger.error('[BaseModel]: {}'.format(e))
            raise e
        except Exception as e:
            raise e
        else:
            return result
Пример #22
0
    def _parse_data(self):
        def _decode_cstring(cstring, encoding='ascii'):
            if type(cstring) is bytes:
                return cstring.decode(encoding).strip('\x00')[::-1]
            else:
                return cstring
        try:
            # remaining part of packet, that contains account name and byte with size of it
            packet_part_with_acc = (len(self.packet) - LoginChallenge.PACKET_SIZE_WITHOUT_ACC_NAME)
            parsed_data = unpack(LoginChallenge.LOGIN_CHAL_PACKET_FORMAT % packet_part_with_acc, self.packet)

            self.unk_code = parsed_data[0]
            self.size = parsed_data[1]
            self.game_name = _decode_cstring(parsed_data[2])
            self.version_major = parsed_data[4]
            self.version_minor = parsed_data[5]
            self.version_patch = parsed_data[6]
            self.version_build = parsed_data[7]
            self.platform = _decode_cstring(parsed_data[8])
            self.os = _decode_cstring(parsed_data[10])
            self.locale = _decode_cstring(parsed_data[11])
            self.timezone = parsed_data[12]
            self.ip_addr = parsed_data[15:19]
            self.account_name_size, account_name = parsed_data[19:]
            self.account_name = account_name.decode('ascii')
        except UnicodeDecodeError as e:
            Logger.error('[Login Challenge]: UnicodeDecodeError: {}'.format(e))

        except StructError as e:
            Logger.error('[Login Challenge]: StructError: {}'.format(e))

        except Exception as e:
            Logger.error('[Login Challenge]: exception({})'.format(e.__class__))
Пример #23
0
    def set_default_skills(self, player: Player):
        try:
            default_skills: List[DefaultSkill] = self.session\
                .query(DefaultSkill)\
                .filter(or_(DefaultSkill.race == player.race, DefaultSkill.char_class == player.char_class))\
                .all()

            skills = []

            for default_skill in default_skills:
                skill = PlayerSkill()
                skill.skill_template = default_skill.skill_template
                skill.player = self.session.merge(player)
                skills.append(skill)

            self.session.add_all(skills)
            self.session.commit()

        except Exception as e:
            Logger.error('[SkillMgr]: {}'.format(e))
        finally:
            return self
Пример #24
0
 async def process(self):
     guid = int.from_bytes(self.packet[6:], 'little')
     Logger.error('[Active Mover]: guid = {}'.format(guid))
     return None, None
Пример #25
0
    QueuesRegistry.session_keys_queue = asyncio.Queue()
    QueuesRegistry.players_queue = asyncio.Queue()
    QueuesRegistry.remove_player_queue = asyncio.Queue()
    QueuesRegistry.connections_queue = asyncio.Queue()
    QueuesRegistry.disconnect_queue = asyncio.Queue()

    QueuesRegistry.packets_queue = asyncio.Queue()
    QueuesRegistry.broadcast_callback_queue = asyncio.Queue()

    try:
        loop.run_until_complete(
            asyncio.gather(
                login_server.get_instance(),
                world_server.get_instance(),
                # websocket_server.get_instance(),
                asyncio.ensure_future(world_manager.run())))
    except Exception as e:
        Logger.error('[Run]: (run until complete) {}'.format(e))
        raise e

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        # TODO: add signal to stop all runned tasks
        pass
    except Exception as e:
        Logger.error('[Run]: (run forever) {}'.format(e))
        raise e
    finally:
        loop.close()
Пример #26
0
    executor = ProcessPoolExecutor(max_workers=cpu_count())

    subprocess.run('redis-server --daemonize yes', shell=True)

    try:
        loop.run_until_complete(
            asyncio.gather(
                login_server.get_instance(),
                world_server.get_instance(),
                asyncio.ensure_future(world_server.refresh_connections()),
                asyncio.ensure_future(
                    world_server.send_update_packet_to_player()),

                # FIXME: anti-pattern
                # https://stackoverflow.com/questions/49275895/asyncio-multiple-concurrent-servers/49280706#49280706
                loop.run_in_executor(executor, WebServer.run),
                asyncio.ensure_future(WorldManager().run())))
    except Exception as e:
        Logger.error('Exception on Run step: {}'.format(e))
        traceback.print_exc()

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    except Exception as e:
        Logger.error('Exception: {}'.format(e))
        pass

    loop.close()