async def decode_login_encryption_response(cls, msg: ClientMessage) ->...:
        # 1.7.x
        if msg.protocol_version <= 5:
            unpack_array = lambda b: b.read(b.unpack('h'))
        else:
            unpack_array = lambda b: b.read(b.unpack_varint(max_bits=16))

        buffer = msg.buffer
        p_shared_secret = unpack_array(buffer)
        p_shared_verify = unpack_array(buffer)
        shared_secret = decrypt_secret(ServerCore.keypair, p_shared_secret)
        shared_verify = decrypt_secret(ServerCore.keypair, p_shared_verify)

        if shared_verify != msg.conn.verify_token:
            msg.close_connection("Invalid verify token!")

        msg.conn.cipher.enable(shared_secret)

        digest = make_digest(msg.conn.server_id.encode("ascii"), shared_secret,
                             ServerCore.pubkey)

        url = ("https://sessionserver.mojang.com/session/minecraft/hasJoined" +
               f"?username={msg.conn.display_name}&serverId={digest}")
        if ServerCore.options["prevent-proxy-connections"]:
            url += f"&ip={msg.conn.client.server_hostname}"

        async with ClientSession() as session:
            async with session.get(url,
                                   timeout=ServerCore.auth_timeout) as resp:
                data = await resp.json()
                msg.conn.uuid = UUID(data["id"])

        cls.update_protocol(msg, "play")
        return PlayerRegistry.add_player(msg.conn)
    async def event_player_join(cls, msg: ClientMessage, player: Player):
        for _player in PlayerRegistry.players:
            # PacketHandler.encode_player_info(msg, player, _player, 0)
            await PacketHandler.encode_chat_message(
                msg, _player, f"\u00a7e{player.display_name} has joined.")

        await PacketHandler.encode_player_join(msg, player)
        await PacketHandler.encode_player_spawn_pos(msg, player)
        await PacketHandler.encode_player_position_look(msg, player)
        await PacketHandler.encode_player_spawn(msg, player, player)
        while msg.conn.do_loop:
            val = random.randint(0, 2**20)
            msg.send_packet(
                "keep_alive",
                msg.buffer_type.pack_varint(val) if msg.protocol_version <= 338
                else msg.buffer_type.pack("Q", val))
            keep_alive_packet = await msg.conn.wait_for_packet("keep_alive")

            # 1.7.x
            if msg.protocol_version <= 338:
                verify = keep_alive_packet.unpack_varint()
            else:
                verify = keep_alive_packet.unpack("Q")

            if val != verify:
                msg.close_connection("Invalid KeepAlive packet!")
                return

            await anyio.sleep(1)
 async def encode_player_join(cls, msg: ClientMessage, player: Player):
     msg.send_packet(
         "join_game",
         msg.buffer_type.pack("iBiBB", player.entity.id, player.gamemode,
                              player.entity.dimension,
                              ServerCore.options["difficulty"],
                              ServerCore.options["max-players"]) +
         msg.buffer.pack_string("default") +
         msg.buffer_type.pack("?", False))
    async def decode_handshake(cls, msg: ClientMessage):
        buffer = msg.buffer
        msg.set_protocol_version(buffer.unpack_varint())
        hostname = buffer.unpack_string()
        port = buffer.unpack("H")
        next_state = buffer.unpack_varint()

        mode = protocol_modes.get(next_state, next_state)
        msg.conn.protocol_state = mode

        cls.update_protocol(msg, mode)
    async def encode_player_position_look(cls, msg: ClientMessage,
                                          player: Player):
        teleport_id = random.randint(0, 2**20)

        msg.send_packet(
            "player_position_and_look",
            msg.buffer_type.pack("ddd", *player.entity.position) +
            msg.buffer_type.pack("ff", *player.entity.rotation) +
            msg.buffer_type.pack("b", player.entity.flags) +
            msg.buffer_type.pack_varint(teleport_id))

        teleport_packet = await msg.conn.wait_for_packet("teleport_confirm")
        verify = teleport_packet.unpack_varint()
        if teleport_id != verify:
            msg.close_connection("Invalid Teleport ID packet")
 async def encode_player_abilities(cls, msg: ClientMessage, player: Player):
     player.conn.send_packet(
         msg.build_packet(
             "player_abilities",
             msg.buffer_type.pack("bff", player.entity.abilities,
                                  player.entity.fly_speed,
                                  player.entity.walk_speed)))
 async def encode_player_spawn(cls, msg: ClientMessage,
                               target_player: Player, player: Player):
     target_player.conn.send_packet(
         msg.build_packet(
             "spawn_player",
             msg.buffer_type.pack_varint(player.entity.id) +
             player.uuid.bytes +
             msg.buffer_type.pack("ddd", *player.entity.position) +
             msg.buffer_type.pack("ff", *player.entity.rotation) +
             msg.buffer_type.pack_entity_metadata(player.entity.metadata)))
    async def decode_login_start(cls, msg: ClientMessage):
        msg.conn.display_name = msg.buffer.unpack_string()

        if ServerCore.options["online-mode"]:
            # 1.7.x
            if msg.protocol_version <= 5:
                pack_array = lambda a: msg.buffer_type.pack('h', len(a)) + a
            else:
                pack_array = lambda a: msg.buffer_type.pack_varint(
                    len(a), max_bits=16) + a

            msg.send_packet(
                "login_encryption_request",
                msg.buffer_type.pack_string(msg.conn.server_id) +
                pack_array(ServerCore.pubkey) +
                pack_array(msg.conn.verify_token))
        else:
            # TODO
            pass
    async def decode_status_request(cls, msg: ClientMessage):
        d = {
            "description": {
                "text": ServerCore.options["motd"]
            },
            "players": {
                "online": PlayerRegistry.player_count(),
                "max": ServerCore.options["max-players"]
            },
            "version": {
                "name": packets.minecraft_versions.get(msg.protocol_version,
                                                       "???"),
                "protocol": msg.protocol_version
            }
        }
        favicon = read_favicon()
        if favicon:
            d["favicon"] = f"data:image/png;base64,{favicon}"

        msg.send_packet("status_response", msg.buffer_type.pack_json(d))
    async def serve_loop(self):
        data = b""
        run_again = False
        async with create_task_group() as tg:
            while self.do_loop:
                if not run_again:
                    try:
                        line = await self.client.receive_some(1024)
                    except ConnectionError:
                        line = b""

                    if line == b"":
                        try:
                            warn(f"Closing connection to {self.client.server_hostname}")
                        except TLSRequired:
                            pass

                        self.do_loop = False
                        break

                    data += self.cipher.decrypt(line)

                try:
                    msg = ClientMessage(self, data, self.protocol_version)
                except BufferUnderrun:
                    run_again = False
                    continue
                else:
                    data = data[msg.old_len:]
                    if data != b"":
                        run_again = True

                for lock in self._locks:
                    if lock["name"] == msg.name:
                        self._locks.remove(lock)
                        lock["result"] = msg.buffer
                        await lock["lock"].set()
                        break

                if msg.name == "handshake":
                    await self.handle_msg(msg)
                else:
                    await tg.spawn(self.handle_msg, msg)

            for lock in self._locks:
                await lock["lock"].set()
            if self.protocol_state == "play":
                # User was logged in
                debug("Player left, removing from game...")
                # TODO: Fix EventHandler
                # Requires: client_message.py:22
                # EventHandler.event_player_leave(self.player)
                PlayerRegistry.players.remove(self.player)
    async def encode_player_info(cls, msg: ClientMessage,
                                 target_player: Player, player_updated: Player,
                                 action: int):
        player_packet = player_updated.uuid.bytes

        display_name = None if player_updated.display_name == player_updated.name else player_updated.display_name

        if action == 0:
            player_packet += (
                msg.buffer_type.pack_string(player_updated.name) +
                msg.buffer_type.pack_varint(len(player_updated.properties)) +
                player_updated.pack_properties(msg.buffer_type) +
                msg.buffer_type.pack_varint(player_updated.gamemode) +
                msg.buffer_type.pack_varint(player_updated.ping) +
                msg.buffer_type.pack(
                    "?", player_updated.display_name != player_updated.name) +
                msg.buffer_type.pack_optional(msg.buffer_type.pack_chat,
                                              display_name))
        elif action == 1:
            player_packet += msg.buffer_type.pack_varint(
                player_updated.gamemode)
        elif action == 2:
            player_packet += msg.buffer_type.pack_varint(player_updated.ping)
        elif action == 3:
            player_packet += (msg.buffer_type.pack(
                "?", player_updated.display_name != player_updated.name) +
                              msg.buffer_type.pack_optional(
                                  msg.buffer_type.pack_chat, display_name))
        elif action == 4:
            pass
        else:
            raise Exception

        target_player.conn.send_packet(
            msg.build_packet(
                "player_list_item",
                msg.buffer_type.pack_varint(action) +
                msg.buffer_type.pack_varint(1) +
                msg.buffer.pack_array("", player_packet)))
    def update_protocol(cls, msg: ClientMessage, mode: str):
        if mode == "play":
            if msg.compression_threshold:
                # Send set compression
                msg.send_packet(
                    "login_set_compression",
                    msg.buffer_type.pack_varint(msg.compression_threshold))

            # Send login success
            msg.send_packet(
                "login_success",
                msg.buffer_type.pack_string(str(msg.conn.uuid)) +
                msg.buffer_type.pack_string(msg.conn.display_name))

        elif mode == "login":
            if PlayerRegistry.player_count(
            ) >= ServerCore.options["max-players"]:
                msg.close_connection("Server is full!")

        msg.conn.protocol_state = mode
 async def encode_chat_message(cls, msg: ClientMessage,
                               target_player: Player, message: str):
     buf = msg.buffer_type.pack_chat(message) + msg.buffer_type.pack("b", 0)
     target_player.conn.send_packet(msg.build_packet("chat_message", buf))
 async def encode_player_spawn_pos(cls, msg: ClientMessage, player: Player):
     msg.send_packet("spawn_position",
                     msg.buffer_type.pack_position(*player.entity.position))
 async def packet_chat_message(cls, msg: ClientMessage):
     message = msg.buffer.unpack_string()
     packet = msg.build_packet("chat_message",
                               msg.buffer_type.pack_chat(message))
     for player in PlayerRegistry.players:
         player.conn.send_packet(packet)
 async def decode_status_ping(cls, msg: ClientMessage):
     time = msg.buffer.unpack("Q")
     msg.send_packet("status_pong", msg.buffer_type.pack("Q", time))