/// <summary>
    /// Refreshes the client's login status
    /// </summary>
    public void RefreshLoginStatus()
    {
        Debug.Log("Checking user login..");
        Debug.Log($"Client auth server token: {MojangAuthentication.GetClientToken()}");

        SetAuthImage(AuthImageStatus.LOADING);
        StartCoroutine(MojangAuthentication.GetLoginStatus(HandleLoginResponse));
    }
    /// <summary>
    /// Logs the user in using the filled-in username and password
    /// </summary>
    public void LoginUser()
    {
        string username = EmailInput.text;
        string password = PasswordInput.text;

        // check that username and password are filled out
        if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
        {
            return;
        }

        SetAuthImage(AuthImageStatus.LOADING);
        StartCoroutine(MojangAuthentication.Login(username, password, HandleLoginResponse));
    }
    /// <summary>
    /// Gets the next packet from the server
    /// </summary>
    /// <returns></returns>
    public PacketData ReadNextPacket()
    {
        int packetId;

        byte[] payload;

        lock (_streamReadLock)
        {
            int         length = VarInt.ReadNext(ReadBytes);
            List <byte> buffer = new List <byte>();

            // check if data is compressed
            if (_compressionThreshold >= 0)
            {
                int dataLength = VarInt.ReadNext(ReadBytes);
                length -= VarInt.GetBytes(dataLength).Length;                   // remove size of data length from rest of packet length
                if (dataLength != 0)
                {
                    byte[] compressedBuffer = ReadBytes(length);
                    buffer.AddRange(ZlibStream.UncompressBuffer(compressedBuffer));
                }
                else
                {
                    buffer.AddRange(ReadBytes(length));
                }
            }
            else
            {
                buffer.AddRange(ReadBytes(length));
            }

            packetId = VarInt.ReadNext(buffer);
            payload  = buffer.ToArray();
        }

        // handles some stuff during login phase
        if (State == ProtocolState.LOGIN)
        {
            // handle compression packet
            if (packetId == (int)ClientboundIDs.LOGIN_SET_COMPRESSION)
            {
                _compressionThreshold = VarInt.ReadNext(new List <byte>(payload));
                return(ReadNextPacket());
            }

            // handle protocol encryption packet
            if (packetId == (int)ClientboundIDs.LOGIN_ENCRYPTION_REQUEST)
            {
                var encRequestPkt = new EncryptionRequestPacket()
                {
                    Payload = payload
                };

                var aesSecret = CryptoHandler.GenerateSharedSecret();
                var authHash  = CryptoHandler.SHAHash(Encoding.ASCII.GetBytes(encRequestPkt.ServerID).Concat(aesSecret, encRequestPkt.PublicKey));

                Debug.Log($"Sending hash to Mojang servers: {authHash}");

                // check session with mojang
                if (!MojangAuthentication.JoinServer(authHash))
                {
                    throw new UnityException("Invalid session. (Try restarting game or relogging into Minecraft account)");
                }

                // use pub key to encrypt shared secret
                var    rsaProvider = CryptoHandler.DecodeRSAPublicKey(encRequestPkt.PublicKey);
                byte[] encSecret   = rsaProvider.Encrypt(aesSecret, false);
                byte[] encToken    = rsaProvider.Encrypt(encRequestPkt.VerifyToken, false);

                // respond to server with private key
                var responsePkt = new EncryptionResponsePacket()
                {
                    SharedSecret = encSecret,
                    VerifyToken  = encToken
                };
                WritePacket(responsePkt);

                // enable aes encryption
                _aesStream = new AesStream(Client.GetStream(), aesSecret);
                _encrypted = true;

                // read the next packet
                return(ReadNextPacket());
            }
        }

        return(new PacketData
        {
            ID = packetId,
            Payload = payload
        });
    }