/// <summary>
        /// Return false if disconnect/finished
        /// </summary>
        void RunClientHandshake()
        {
            clientThread.WatchdogTick = DateTime.Now;

            //Read Handshake
            var hs = PacketReader.ReadFirstPackage(clientStream);
            if (hs == null)
            {
                //Old status packet
                LegacyStatus.SendStatus(clientStream);
                Phase = Phases.FinalClose;
                return;
            }
            var h = new Handshake(hs);
            Debug.FromClient(this, h);
            clientThread.State = "Handshake Received " + h;
            ClientVersion = h.Version;
            if (h.State == HandshakeState.Status)
            {
                RunStatusPing(h);
                return;
            }
            if (h.State == HandshakeState.None)
            {
                Close("Invalid handshake state: " + h.State);
                return;
            }
            if (h.State != HandshakeState.Login)
            {
                Close("Invalid handshake state: " + h.State);
                return;
            }

            #if DEBUG
            if (h.Version >= ProtocolVersion.next)
                throw new InvalidDataException("new version: " + h.Version);
            #endif
            if (h.Version > MinecraftServer.BackendVersion)
            {
                clientThread.State = "Handshake too high version";
                Close("We are still running " + MinecraftServer.BackendVersion.ToText() + " we are so very sorry about that");
                return;
            }
            if (h.Version < MinecraftServer.FrontendVersion)
            {
                clientThread.State = "Handshake too low version";
                Close("Upgrade your client to " + MinecraftServer.FrontendVersion.ToText());
                return;
            }

            clientThread.WatchdogTick = DateTime.Now;

            //Login
            var l = new LoginStart(PacketReader.ReadHandshake(clientStream));
            Debug.FromClient(this, l);

            clientThread.State = "LoginStart Received " + l.Name;
            unverifiedUsername = l.Name;
            if (unverifiedUsername.Length == 0 || unverifiedUsername.Length > 16)
            {
                clientThread.State = "Handshake wrong username length";
                Close("Bad username");
                Console.WriteLine("Wrong username length: " + unverifiedUsername.Length);
                return;
            }

            //Set Compression
            var compression = new SetCompression();
            SendToClientInternal(compression);
            maxUncompressed = compression.MaxSize;

            //Send encryption request
            clientThread.State = "Handshake: Sending encryption request";
            //Initiate ID
            var r = new Random();
            ID = new byte[8];
            r.NextBytes(ID);
            SendToClientInternal(new EncryptionRequest(Authentication.McHex(ID), MinecraftServer.RsaBytes));
            clientThread.State = "Handshake: Sent encryption request, reading response";

            //Read enc response
            int erSize;
            var erBuffer = PacketReader.Read(clientStream, out erSize);
            if (erSize != 0)
                erBuffer = Compression.Decompress(erBuffer, erSize);
            var er = new EncryptionResponse(erBuffer);
            Debug.FromClient(this, er);

            clientThread.State = "Handshake: Got enc resp";
            CryptoMC cryptoStream = new CryptoMC(clientStream, er);

            //Verify user
            clientThread.State = "Handshake: loading proxy data";
            Settings = LoadProxyPlayer(unverifiedUsername);

            clientThread.State = "Handshake: Verifying login credentials";
            var auth = Authentication.VerifyUserLogin(unverifiedUsername, cryptoStream.SharedKey, ID);
            #warning From here we now need to take care of the id in the response.
            if (auth == null)
            {
                //Unauthorized
                Log.WriteAuthFail(unverifiedUsername, RemoteEndPoint, "");
                Close("Mojang says no");
                return;
            }
            else
            {
                MinecraftUsername = unverifiedUsername;
                Log.WritePlayer(this, "Login from " + RemoteEndPoint + " " + Country);
            }
            clientThread.State = "Handshake: Logged in";

            //Get UUID
            Settings.UUID = auth.id;

            clientThread.WatchdogTick = DateTime.Now;

            MineProxy.Inbox.Status(this);

            //start encryption
            clientStream = cryptoStream;

            SendToClientInternal(new LoginSuccess(Settings.UUID, MinecraftUsername));

            clientThread.User = MinecraftUsername;
            clientThread.State = "Logged in";

            EntityID = freeEID;
            
            //Math.Abs(unverifiedUsername.GetHashCode());
            freeEID += 5000;

            //Login reply to client
            //No longer, send the vanilla server Login reply instead.

            Phase = Phases.Gaming;
            Queue = new SendQueue(this);

            PlayerList.LoginPlayer(this);

            SaveProxyPlayer();

            string name = h.Host.ToLowerInvariant().Split('.')[0];
            if (World.VanillaWorlds.ContainsKey(name))
                SetWorld(World.VanillaWorlds[name]);
            else
                SetWorld(World.Main);
            //Handshake complete
        }
        /// <summary>
        /// Throw exception on error
        /// </summary>
        void RunServerHandshake()
        {
            thread.WatchdogTick = DateTime.Now;

            //Handshaker
            SendToBackend(new Handshake(MinecraftServer.IP.ToString(), Vanilla.Endpoint.Port, HandshakeState.Login));
            SendToBackend(new LoginStart(Player.MinecraftUsername));

            thread.WatchdogTick = DateTime.Now;

            //No encryption with localhost?!!! :D
            /*
            //Read Encryption Request
            var encReq = new EncryptionRequest(PacketReader.Read(serverReader, EncryptionRequest.ID));
            Debug.FromServer(encReq, Player);

            //Send response
            if (encReq.ServerID != "")
                throw new InvalidOperationException("Backend server online mode must be false, ID=-, got " + encReq.ServerID);
            crypto = new CryptoMC(serverStream, encReq);
            SendToBackend(new EncryptionResponse(crypto));

            //Start encryption
            serverStream = crypto;
            serverReader = new EndianBinaryReader(EndianBitConverter.Big, serverStream);
            serverWriter = new EndianBinaryWriter(EndianBitConverter.Big, serverStream);
            */

            byte[] buffer = PacketReader.ReadHandshake(serverStream);
            if (buffer[0] == 0)
            {
                string disconnectMessage = Encoding.ASCII.GetString(buffer, 2, buffer.Length - 2);
                Player.TellSystem("[Error]", disconnectMessage);
                throw new InvalidOperationException(disconnectMessage);
            }

            var compression = new SetCompression(buffer);
            maxUncompressed = compression.MaxSize;
            Debug.FromServer(compression, Player);

            var loginSuccess = new LoginSuccess(PacketReader.Read(serverStream));
            OfflineUUID = loginSuccess.UUID;
            Debug.FromServer(loginSuccess, Player);

            //Read LoginRequest
            JoinGame res = new JoinGame(PacketReader.Read(serverStream));
            Debug.FromServer(res, Player);

            thread.State = "Got Login";
            EID = res.EntityID;
            Mode = (GameMode)res.GameMode;
            Dimension = res.Dimension;

            //Store UUID permanently - No need to store from backend since that is offline uuid

            phase = Phases.Gaming;

            //Send server JoinGame to the client
            Player.EntityID = res.EntityID;
            FromServerGaming(res);

            Cloak.SetCloak(Player, null);
        }