internal Task StartAsync() { // Let's announce our intentions to the server var vdp = new VoiceDispatch(); if (!this.Resume) { vdp.OpCode = 0; vdp.Payload = new VoiceIdentifyPayload { ServerId = this.ServerData.GuildId, UserId = this.StateData.UserId.Value, SessionId = this.StateData.SessionId, Token = this.ServerData.Token }; this.Resume = true; } else { vdp.OpCode = 7; vdp.Payload = new VoiceIdentifyPayload { ServerId = this.ServerData.GuildId, SessionId = this.StateData.SessionId, Token = this.ServerData.Token }; } var vdj = JsonConvert.SerializeObject(vdp, Formatting.None); this.VoiceWs.SendMessage(vdj); return(Task.Delay(0)); }
private async Task HeartbeatAsync() { await Task.Yield(); var token = Token; while (true) { try { token.ThrowIfCancellationRequested(); var dt = DateTime.Now; Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", "Sent heartbeat", dt); var hbd = new VoiceDispatch { OpCode = 3, Payload = UnixTimestamp(dt) }; var hbj = JsonConvert.SerializeObject(hbd); VoiceWs.SendMessage(hbj); LastHeartbeat = dt; await Task.Delay(HeartbeatInterval).ConfigureAwait(false); } catch (OperationCanceledException) { return; } } }
/// <summary> /// Sends a speaking status to the connected voice channel. /// </summary> /// <param name="speaking">Whether the current user is speaking or not.</param> /// <returns>A task representing the sending operation.</returns> public async Task SendSpeakingAsync(bool speaking = true) { if (!this.IsInitialized) { throw new InvalidOperationException("The connection is not yet initialized"); } if (!speaking) { this.Synchronizer.Reset(); } var pld = new VoiceDispatch { OpCode = 5, Payload = new VoiceSpeakingPayload { Speaking = speaking, Delay = 0 } }; var plj = JsonConvert.SerializeObject(pld, Formatting.None); await Task.Run(() => this.VoiceWs.SendMessage(plj)); }
internal async Task StartAsync() { // Let's announce our intentions to the server var vdp = new VoiceDispatch(); if (!this.Resume) { vdp.OpCode = 0; vdp.Payload = new VoiceIdentifyPayload { ServerId = this.ServerData.GuildId, UserId = this.StateData.UserId.Value, SessionId = this.StateData.SessionId, Token = this.ServerData.Token }; this.Resume = true; } else { vdp.OpCode = 7; vdp.Payload = new VoiceIdentifyPayload { ServerId = this.ServerData.GuildId, SessionId = this.StateData.SessionId, Token = this.ServerData.Token }; } var vdj = JsonConvert.SerializeObject(vdp, Formatting.None); await this.WsSendAsync(vdj).ConfigureAwait(false); }
private void Heartbeat() { while (true) { try { var dt = DateTime.Now; this.Discord.DebugLogger.LogMessage(LogLevel.Unnecessary, "VoiceNext", "Sent heartbeat", dt); var hbd = new VoiceDispatch { OpCode = 3, Payload = UnixTimestamp(dt) }; var hbj = JsonConvert.SerializeObject(hbd); this.VoiceWs.SendMessage(hbj); this.LastHeartbeat = dt; Thread.Sleep(this.HeartbeatInterval); } catch (ThreadAbortException) { return; } } }
private async Task HeartbeatAsync() { await Task.Yield(); var token = this.Token; while (true) { try { token.ThrowIfCancellationRequested(); var dt = DateTime.Now; this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHeartbeat, "Sent heartbeat"); var hbd = new VoiceDispatch { OpCode = 3, Payload = UnixTimestamp(dt) }; var hbj = JsonConvert.SerializeObject(hbd); await this.WsSendAsync(hbj).ConfigureAwait(false); this.LastHeartbeat = dt; await Task.Delay(this.HeartbeatInterval).ConfigureAwait(false); } catch (OperationCanceledException) { return; } } }
private async Task Stage1() { // Begin heartbeating this.HeartbeatTask = Task.Run(this.Heartbeat); #if !NETSTANDARD1_1 // IP Discovery this.UdpClient.Setup(this.ConnectionEndpoint); var pck = new byte[70]; Array.Copy(BitConverter.GetBytes(this.SSRC), 0, pck, pck.Length - 4, 4); await this.UdpClient.SendAsync(pck, pck.Length); var ipd = await this.UdpClient.ReceiveAsync(); var ipe = Array.IndexOf <byte>(ipd, 0, 4); var ip = new UTF8Encoding(false).GetString(ipd, 4, ipe - 4); var port = BitConverter.ToUInt16(ipd, ipd.Length - 2); this.DiscoveredEndpoint = new IpEndpoint { Address = System.Net.IPAddress.Parse(ip), Port = port }; #endif // Ready var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { #if !NETSTANDARD1_1 Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, #else Address = "0.0.0.0", Port = 0, #endif Mode = VOICE_MODE } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); this.VoiceWs.SendMessage(vsj); #if !NETSTANDARD1_1 if (this.Configuration.EnableIncoming) { this.ReceiverTask = Task.Run(this.VoiceReceiverTask, this.Token); } #endif }
/// <summary> /// Connects this Lavalink node to specified Discord channel. /// </summary> /// <param name="channel">Voice channel to connect to.</param> /// <returns>Channel connection, which allows for playback control.</returns> public async Task <LavalinkGuildConnection> ConnectAsync(DiscordChannel channel) { if (this._connectedGuilds.ContainsKey(channel.Guild.Id)) { return(this._connectedGuilds[channel.Guild.Id]); } if (channel.Guild == null || channel.Type != ChannelType.Voice) { throw new ArgumentException("Invalid channel specified.", nameof(channel)); } var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>(); var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>(); this.VoiceStateUpdates[channel.Guild.Id] = vstut; this.VoiceServerUpdates[channel.Guild.Id] = vsrut; var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = channel.Guild.Id, ChannelId = channel.Id, Deafened = false, Muted = false } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); await(channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false); var vstu = await vstut.Task.ConfigureAwait(false); var vsru = await vsrut.Task.ConfigureAwait(false); await this.SendPayloadAsync(new LavalinkVoiceUpdate(vstu, vsru)).ConfigureAwait(false); var con = new LavalinkGuildConnection(this, channel, vstu); con.ChannelDisconnected += this.Con_ChannelDisconnected; con.PlayerUpdated += (s, e) => this._playerUpdated.InvokeAsync(s, e); con.PlaybackStarted += (s, e) => this._playbackStarted.InvokeAsync(s, e); con.PlaybackFinished += (s, e) => this._playbackFinished.InvokeAsync(s, e); con.TrackStuck += (s, e) => this._trackStuck.InvokeAsync(s, e); con.TrackException += (s, e) => this._trackException.InvokeAsync(s, e); this._connectedGuilds[channel.Guild.Id] = con; return(con); }
internal async Task SendVoiceUpdateAsync() { var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = this.GuildId, ChannelId = null, Deafened = false, Muted = false } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); await(this.Channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false); }
internal void SendVoiceUpdate() { var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = this.GuildId, ChannelId = null, Deafened = false, Muted = false } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); (this.Channel.Discord as DiscordClient)._webSocketClient.SendMessage(vsj); }
/// <summary> /// Sends a speaking status to the connected voice channel. /// </summary> /// <param name="speaking">Whether the current user is speaking or not.</param> /// <returns>A task representing the sending operation.</returns> public async Task SendSpeakingAsync(bool speaking = true) { if (!IsInitialized) { throw new InvalidOperationException("The connection is not initialized"); } if (!speaking) { var nullpcm = new byte[3840]; for (var i = 0; i < 5; i++) { await SendAsync(nullpcm, 20).ConfigureAwait(false); } SynchronizerTicks = 0; if (PlayingWait != null) { PlayingWait.SetResult(true); } } else { if (PlayingWait == null || PlayingWait.Task.IsCompleted) { PlayingWait = new TaskCompletionSource <bool>(); } } var pld = new VoiceDispatch { OpCode = 5, Payload = new VoiceSpeakingPayload { Speaking = speaking, Delay = 0 } }; var plj = JsonConvert.SerializeObject(pld, Formatting.None); VoiceWs.SendMessage(plj); }
private async Task Stage1() { // Begin heartbeating this.HeartbeatThread = new Thread(this.Heartbeat); this.HeartbeatThread.SetApartmentState(ApartmentState.STA); this.HeartbeatThread.Name = $"Heartbeat for Voice ({this.SSRC})"; this.HeartbeatThread.Start(); // IP Discovery this.UdpClient.Connect(this.ConnectionEndpoint.Host, this.ConnectionEndpoint.Port); this.UdpClient.AllowNatTraversal(true); var pck = new byte[70]; Array.Copy(BitConverter.GetBytes(this.SSRC), 0, pck, pck.Length - 4, 4); await this.UdpClient.SendAsync(pck, pck.Length); var ipd = await this.UdpClient.ReceiveAsync(); var ipe = Array.IndexOf <byte>(ipd.Buffer, 0, 4); var ip = new UTF8Encoding(false).GetString(ipd.Buffer, 4, ipe - 4); var port = BitConverter.ToUInt16(ipd.Buffer, ipd.Buffer.Length - 2); this.DiscoveredEndpoint = new IPEndPoint(IPAddress.Parse(ip), port); // Ready var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, Mode = VOICE_MODE } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); this.VoiceWs.SendMessage(vsj); }
/// <summary> /// Sends a speaking status to the connected voice channel. /// </summary> /// <param name="speaking">Whether the current user is speaking or not.</param> /// <returns>A task representing the sending operation.</returns> public async Task SendSpeakingAsync(bool speaking = true) { if (!this.IsInitialized) { throw new InvalidOperationException("The connection is not initialized"); } var pld = new VoiceDispatch { OpCode = 5, Payload = new VoiceSpeakingPayload { Speaking = speaking, Delay = 0 } }; var plj = JsonConvert.SerializeObject(pld, Formatting.None); await this.VoiceWs.SendMessageAsync(plj).ConfigureAwait(false); }
/// <summary> /// Sends a speaking status to the connected voice channel. /// </summary> /// <param name="speaking">Whether the current user is speaking or not.</param> /// <returns>A task representing the sending operation.</returns> public void SendSpeaking(bool speaking = true) { if (!this.IsInitialized) { throw new InvalidOperationException("The connection is not initialized"); } var pld = new VoiceDispatch { OpCode = 5, Payload = new VoiceSpeakingPayload { Speaking = speaking, Delay = 0 } }; var plj = JsonConvert.SerializeObject(pld, Formatting.None); this.VoiceWs.SendMessage(plj); }
private void Vnc_VoiceDisconnected(DiscordGuild guild) { var vnc = (VoiceNextConnection)null; if (this.ActiveConnections.ContainsKey(guild.ID)) { this.ActiveConnections.TryRemove(guild.ID, out vnc); } var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = guild.ID, ChannelId = null } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); DiscordClient._websocketClient.SendMessage(vsj); }
private async Task Vnc_VoiceDisconnected(DiscordGuild guild) { VoiceNextConnection vnc = null; if (this.ActiveConnections.ContainsKey(guild.Id)) { this.ActiveConnections.TryRemove(guild.Id, out vnc); } var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = guild.Id, ChannelId = null } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); await(guild.Discord as DiscordClient)._webSocketClient.SendMessageAsync(vsj).ConfigureAwait(false); }
private async Task Stage1(VoiceReadyPayload voiceReady) { // IP Discovery this.UdpClient.Setup(this.UdpEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); this.DiscoveredEndpoint = new IpEndpoint { Address = ip, Port = port }; this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint dicovery finished - discovered endpoint is {0}:{1}", ip, port); void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { var packetSpan = packet.AsSpan(); var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); await this.WsSendAsync(vsj).ConfigureAwait(false); this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }
private async Task Stage1(VoiceReadyPayload voiceReady) { // IP Discovery this.UdpClient.Setup(this.UdpEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); // fetch endpoint it it wasn't done already IpEndpoint ownEndpoint; try { var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); ownEndpoint = new IpEndpoint { Address = ip, Port = port }; this.DiscoveredEndpoint = ownEndpoint; this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now); } catch (Exception e) { this.Discord.DebugLogger.LogMessage(LogLevel.Error, "VNext UDP", "Endpoint discovery failed", DateTime.Now, e); // fallback to last value if present if (this.DiscoveredEndpoint.HasValue) { ownEndpoint = this.DiscoveredEndpoint.Value; this.Discord.DebugLogger.LogMessage(LogLevel.Info, "VNext UDP", "Endpoint discovery used old endpoint", DateTime.Now, e); } else { throw e; } } void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { if (packet.Length != 70) { throw new Exception($"Recieved invalid IP discovery data. Expected length 70 but got {packet.Length}"); } var packetSpan = packet.AsSpan(); var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { Address = ownEndpoint.Address.ToString(), Port = (ushort)ownEndpoint.Port, Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); await this.VoiceWs.SendMessageAsync(vsj).ConfigureAwait(false); if (this.SenderTokenSource != null) { this.SenderTokenSource.Cancel(); } this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); if (this.ReceiverTokenSource != null) { this.ReceiverTokenSource.Cancel(); } this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }
/// <summary> /// Create a VoiceNext connection for the specified channel. /// </summary> /// <param name="channel">Channel to connect to.</param> /// <returns>VoiceNext connection for this channel.</returns> public async Task <VoiceNextConnection> ConnectAsync(DiscordChannel channel) { if (channel.Type != ChannelType.Voice) { throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice channel"); } if (channel.Parent == null) { throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel"); } var gld = channel.Parent; if (ActiveConnections.ContainsKey(gld.ID)) { throw new InvalidOperationException("This guild already has a voice connection"); } var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>(); var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>(); this.VoiceStateUpdates[gld.ID] = vstut; this.VoiceServerUpdates[gld.ID] = vsrut; var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = gld.ID, ChannelId = channel.ID, Deafened = false, Muted = false } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); DiscordClient._websocketClient.SendMessage(vsj); var vstu = await vstut.Task; var vstup = new VoiceStateUpdatePayload { SessionId = vstu.SessionID, UserId = vstu.UserID }; var vsru = await vsrut.Task; var vsrup = new VoiceServerUpdatePayload { Endpoint = vsru.Endpoint, GuildId = vsru.GuildID, Token = vsru.VoiceToken }; var d1 = (TaskCompletionSource <VoiceStateUpdateEventArgs>)null; var d2 = (TaskCompletionSource <VoiceServerUpdateEventArgs>)null; this.VoiceStateUpdates.TryRemove(gld.ID, out d1); this.VoiceServerUpdates.TryRemove(gld.ID, out d2); var vnc = new VoiceNextConnection(this.Client, gld, channel, this.Configuration, vsrup, vstup); vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected; await vnc.ConnectAsync(); await vnc.WaitForReady(); this.ActiveConnections[gld.ID] = vnc; return(vnc); }
/// <summary> /// Create a VoiceNext connection for the specified channel. /// </summary> /// <param name="channel">Channel to connect to.</param> /// <returns>VoiceNext connection for this channel.</returns> public async Task <VoiceNextConnection> ConnectAsync(DiscordChannel channel) { if (channel.Type != ChannelType.Voice) { throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice channel"); } if (channel.Guild == null) { throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel"); } if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(Permissions.UseVoice)) { throw new InvalidOperationException("You need UseVoice permission to connect to this voice channel"); } var gld = channel.Guild; if (ActiveConnections.ContainsKey(gld.Id)) { throw new InvalidOperationException("This guild already has a voice connection"); } var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>(); var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>(); this.VoiceStateUpdates[gld.Id] = vstut; this.VoiceServerUpdates[gld.Id] = vsrut; var vsd = new VoiceDispatch { OpCode = 4, Payload = new VoiceStateUpdatePayload { GuildId = gld.Id, ChannelId = channel.Id, Deafened = false, Muted = false } }; var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); await(channel.Discord as DiscordClient)._webSocketClient.SendMessageAsync(vsj).ConfigureAwait(false); var vstu = await vstut.Task.ConfigureAwait(false); var vstup = new VoiceStateUpdatePayload { SessionId = vstu.SessionId, UserId = vstu.User.Id }; var vsru = await vsrut.Task.ConfigureAwait(false); var vsrup = new VoiceServerUpdatePayload { Endpoint = vsru.Endpoint, GuildId = vsru.Guild.Id, Token = vsru.VoiceToken }; var vnc = new VoiceNextConnection(this.Client, gld, channel, this.Configuration, vsrup, vstup); vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected; await vnc.ConnectAsync().ConfigureAwait(false); await vnc.WaitForReadyAsync().ConfigureAwait(false); this.ActiveConnections[gld.Id] = vnc; return(vnc); }
private async Task Stage1(VoiceReadyPayload voiceReady) { #if !NETSTANDARD1_1 // IP Discovery this.UdpClient.Setup(this.ConnectionEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); this.DiscoveredEndpoint = new IpEndpoint { Address = ip, Port = port }; this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now); void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { var packetSpan = packet.AsSpan(); var ipString = new UTF8Encoding(false).GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } #else this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Voice receive not supported - not performing endpoint discovery", DateTime.Now); await Task.Yield(); // just stop bothering me VS #endif // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { #if !NETSTANDARD1_1 Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, #else Address = "0.0.0.0", Port = 0, #endif Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); this.VoiceWs.SendMessage(vsj); this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }