private void KeepAlive()
        {
            try
            {
                while (socket != null)
                {
                    Thread.Sleep(heartbeat);

                    PayloadArgs <long> args = new PayloadArgs <long>()
                    {
                        op = 3,
                        d  = (DateTime.UtcNow.Ticks - 621355968000000000L) / TimeSpan.TicksPerMillisecond
                    };

                    Debugger.WriteLine("VoiceBeat: " + JsonUtility.ToJson(args));
                    socket.Send(JsonUtility.ToJson(args));
                    Debugger.WriteLine("VOICE {BEAT}.");
                    Thread.Sleep(heartbeat);
                }
            }

            catch (Exception e)
            {
                Debug.LogError("Voice KeepAlive: " + e);
                return;
            }
        }
        private void SetSpeaking(bool speaking)
        {
            if (socket != null)
            {
                PayloadArgs <VoiceSpeakingArgs> args = new PayloadArgs <VoiceSpeakingArgs>()
                {
                    op = 5,
                    d  = new VoiceSpeakingArgs()
                    {
                        speaking = speaking,
                        delay    = 0
                    }
                };

                Debugger.WriteLine("Voice Send: " + JsonUtility.ToJson(args));
                socket.Send(JsonUtility.ToJson(args));

                if (!speaking)
                {
                    for (int x = 0; x < 5; x++)
                    {
                        voiceToSend.Enqueue(null);
                    }
                }
            }
        }
        /// <summary> Stops this voiceclient. </summary>
        public void Stop(DiscordVoiceCallback callback)
        {
            PayloadArgs <VoiceDisconnectArgs> args = new PayloadArgs <VoiceDisconnectArgs>()
            {
                op = 4,
                d  = new VoiceDisconnectArgs()
                {
                    guild_id   = channel.serverID,
                    channel_id = null,
                    self_mute  = false,
                    self_deaf  = false
                }
            };

            parent.socket.Send(JsonUtility.ToJson(args));
            parent.unityInvoker.Enqueue(() => callback(parent, this, new DiscordError()));
            socket.CloseAsync();
            parent.voiceClients.Remove(channel.serverID);
        }
        internal void Start(DiscordServer pserver, string endpoint, string voiceToken)
        {
            server  = pserver;
            token   = voiceToken;
            gateway = endpoint;
            socket  = new WebSocket("wss://" + gateway.Split(':')[0]);

            socket.OnMessage += (s, message) =>
            {
                try
                {
                    PayloadJSON e            = JsonUtility.FromJson <PayloadJSON>(message.Data);
                    int         payloadIndex = message.Data.IndexOf("\"d\":{");
                    string      payload      = message.Data.Substring(payloadIndex + 4, message.Data.Length - payloadIndex - 5);
                    Debugger.WriteLine("VOICE {" + e.op + "}: " + payload);
                    ProcessMessage(e.op, payload);
                }

                catch (Exception e)
                {
                    Debug.LogError("Received Error OnMessage: " + e.Message);
                    Debug.LogError("Received Error OnMessage: " + e.Source);
                    Debug.LogError("Received Error OnMessage: " + e.StackTrace);
                    Debugger.WriteLine("VOICE {ERROR}: " + message.Data);
                }
            };

            socket.OnOpen += (sender, e) =>
            {
                PayloadArgs <DiscordIdentifyArgs> args = new PayloadArgs <DiscordIdentifyArgs>()
                {
                    op = 0,
                    d  = new DiscordIdentifyArgs()
                    {
                        server_id  = server.ID,
                        user_id    = parent.user.ID,
                        session_id = parent.sessionID,
                        token      = token
                    }
                };

                socket.Send(JsonUtility.ToJson(args));
            };

            socket.OnError += (s, e) =>
            {
                Debug.LogError(e.Message);
            };

            socket.OnClose += (s, e) =>
            {
                if (!e.WasClean)
                {
                    Debug.LogError(e.Code);
                    Debug.LogError(e.Reason);
                }

                isOnline = false;
                Dispose();
            };

            socket.Connect();
        }
        private void ConnectUDP()
        {
            try
            {
                client = new UdpClient(port);
                client.DontFragment = false;
                client.Connect(gateway.Replace(":80", ""), port);
                endpoint = new IPEndPoint(Dns.GetHostAddresses(gateway.Replace(":80", ""))[0], 80);

                byte[] packet = new byte[70];
                packet[0] = (byte)((ssrc >> 24) & 0xFF);
                packet[1] = (byte)((ssrc >> 16) & 0xFF);
                packet[2] = (byte)((ssrc >> 8) & 0xFF);
                packet[3] = (byte)((ssrc >> 0) & 0xFF);
                client.Send(packet, packet.Length);
                byte[] returnBuffer = client.Receive(ref endpoint);

                if (returnBuffer != null && returnBuffer.Length > 0)
                {
                    int start = 4;
                    int end   = 4;

                    for (int i = start; i < returnBuffer.Length; i++)
                    {
                        if (returnBuffer[i] != (byte)0)
                        {
                            end++;
                        }
                        else
                        {
                            break;
                        }
                    }

                    byte[] buffer = new byte[end - start];
                    Buffer.BlockCopy(returnBuffer, start, buffer, 0, buffer.Length);
                    IPAddress ip = IPAddress.Parse(System.Text.Encoding.ASCII.GetString(buffer));
                    int       p  = returnBuffer[returnBuffer.Length - 2] | returnBuffer[returnBuffer.Length - 1] << 8;
                    endpoint = new IPEndPoint(ip, p);

                    PayloadArgs <VoiceSendIPArgs> args = new PayloadArgs <VoiceSendIPArgs>()
                    {
                        op = 1,
                        d  = new VoiceSendIPArgs()
                        {
                            protocol = "udp",
                            data     = new VoiceSendIPDataArgs()
                            {
                                address = ip.ToString(),
                                port    = p,
                                mode    = encryptionMode
                            }
                        }
                    };

                    Debugger.WriteLine("Voice Send: " + JsonUtility.ToJson(args));
                    socket.Send(JsonUtility.ToJson(args));
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
                Debug.LogError(e.StackTrace);
                Debug.LogError(e.Source);
            }
        }
        private void ProcessMessage(int op, string payload)
        {
            switch (op)
            {
            case 2:
                VoiceConnectionJSON connection = JsonUtility.FromJson <VoiceConnectionJSON>(payload);
                users.Add(parent.user, connection.ssrc);

                for (int i = 0; i < connection.modes.Length; i++)
                {
                    if (!connection.modes[i].ToLower().Contains("plain"))
                    {
                        encryptionMode = connection.modes[i];
                        break;
                    }
                }

                ssrc            = connection.ssrc;
                port            = connection.port;
                heartbeat       = connection.heartbeat_interval;
                heartbeatThread = new Thread(KeepAlive);
                heartbeatThread.Start();
                ConnectUDP();
                break;

            case 3:
                // KeepAlive echo, ignore
                break;

            case 4:
                VoiceKeyJSON e = JsonUtility.FromJson <VoiceKeyJSON>(payload);
                key = e.secret_key;

                PayloadArgs <VoiceSpeakingArgs> args = new PayloadArgs <VoiceSpeakingArgs>()
                {
                    op = 5,
                    d  = new VoiceSpeakingArgs()
                    {
                        speaking = true,
                        delay    = 0
                    }
                };

                socket.Send(JsonUtility.ToJson(args));
                udpThread = new Thread(UDPKeepAlive);
                udpThread.Start();
                isOnline = true;
                parent.unityInvoker.Enqueue(() => startcallback(parent, this, new DiscordError()));

                sendThread    = new Thread(SendVoiceLoop);
                receiveThread = new Thread(ReceiveVoiceLoop);
                sendThread.Start();
                receiveThread.Start();

                SetSpeaking(true);
                break;

            case 5:
                VoiceSpeakingJSON speaker = JsonUtility.FromJson <VoiceSpeakingJSON>(payload);
                DiscordUser       user    = server._members[speaker.user_id];

                if (!users.ContainsKey(user))
                {
                    users.Add(user, speaker.ssrc);
                    parent.unityInvoker.Enqueue(() => OnVoiceUserSpeaking(this, new DiscordUserSpeakingArgs()
                    {
                        client = parent, speaking = speaker.speaking, user = user
                    }));
                }

                else
                {
                    parent.unityInvoker.Enqueue(() => OnVoiceUserSpeaking(this, new DiscordUserSpeakingArgs()
                    {
                        client = parent, speaking = speaker.speaking, user = user
                    }));
                }

                break;
            }
        }