@@ -4,6 +4,7 @@ using Discord.API.Client.VoiceSocket;
using Discord.Audio;
using Discord.Audio;
using Discord.Audio.Opus;
using Discord.Audio.Opus;
using Discord.Audio.Sodium;
using Discord.Audio.Sodium;
using Discord.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using System;
using System;
@@ -24,35 +25,33 @@ namespace Discord.Net.WebSockets
private const int MaxOpusSize = 4000;
private const int MaxOpusSize = 4000;
private const string EncryptedMode = "xsalsa20_poly1305";
private const string EncryptedMode = "xsalsa20_poly1305";
private const string UnencryptedMode = "plain";
private const string UnencryptedMode = "plain";
//private readonly Random _rand;
private readonly int _targetAudioBufferLength;
private readonly int _targetAudioBufferLength;
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
private readonly DiscordAudioClient _audioClient;
private readonly DiscordAudioClient _audioClient;
private readonly AudioServiceConfig _config;
private readonly AudioServiceConfig _config;
private OpusEncoder _encoder;
private Thread _sendThread, _receiveThread;
private VoiceBuffer _sendBuffer;
private OpusEncoder _encoder;
private uint _ssrc;
private uint _ssrc;
private ConcurrentDictionary<uint, ulong> _ssrcMapping;
private ConcurrentDictionary<uint, ulong> _ssrcMapping;
private VoiceBuffer _sendBuffer;
private UdpClient _udp;
private UdpClient _udp;
private IPEndPoint _endpoint;
private IPEndPoint _endpoint;
private bool _isEncrypted;
private bool _isEncrypted;
private byte[] _secretKey, _encodingBuffer;
private byte[] _secretKey, _encodingBuffer;
private ushort _sequence;
private ushort _sequence;
private ulong? _serverId, _channelId;
private string _encryptionMode;
private string _encryptionMode;
private int _ping;
private Thread _sendThread, _receiveThread;
public ulong? ServerId { get { return _serverId; } internal set { _serverId = value; } }
public ulong? ChannelId { get { return _channelId; } internal set { _channelId = value; } }
public int Ping => _ping;
private int _ping;
public string Token { get; internal set; }
public ulong? ServerId { get; internal set; }
public ulong? ChannelId { get; internal set; }
public int Ping => _ping;
internal VoiceBuffer OutputBuffer => _sendBuffer;
internal VoiceBuffer OutputBuffer => _sendBuffer;
public VoiceWebSocket(DiscordClient client, DiscordAudioClient audioClient, Logger logger)
: base(client, logger)
public VoiceWebSocket(DiscordClient client, DiscordAudioClient audioClient, JsonSerializer serializer, Logger logger)
: base(client, serializer, logger)
{
{
_audioClient = audioClient;
_audioClient = audioClient;
_config = client.Audio().Config;
_config = client.Audio().Config;
@@ -84,7 +83,7 @@ namespace Discord.Net.WebSockets
catch (OperationCanceledException) { throw; }
catch (OperationCanceledException) { throw; }
catch (Exception ex)
catch (Exception ex)
{
{
_l ogger.Error("Reconnect failed", ex);
L ogger.Error("Reconnect failed", ex);
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
}
}
@@ -101,14 +100,14 @@ namespace Discord.Net.WebSockets
List<Task> tasks = new List<Task>();
List<Task> tasks = new List<Task>();
if ((_config.Mode & AudioMode.Outgoing) != 0)
if ((_config.Mode & AudioMode.Outgoing) != 0)
{
{
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_c ancelToken)));
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(C ancelToken)));
_sendThread.IsBackground = true;
_sendThread.IsBackground = true;
_sendThread.Start();
_sendThread.Start();
}
}
if ((_config.Mode & AudioMode.Incoming) != 0)
if ((_config.Mode & AudioMode.Incoming) != 0)
{
{
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_c ancelToken)));
_receiveThread.IsBackground = true;
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(C ancelToken)));
_receiveThread.IsBackground = true;
_receiveThread.Start();
_receiveThread.Start();
}
}
@@ -117,8 +116,8 @@ namespace Discord.Net.WebSockets
#if !DOTNET5_4
#if !DOTNET5_4
tasks.Add(WatcherAsync());
tasks.Add(WatcherAsync());
#endif
#endif
tasks.AddRange(_engine.GetTasks(_c ancelToken));
tasks.Add(HeartbeatAsync(_c ancelToken));
tasks.AddRange(_engine.GetTasks(C ancelToken));
tasks.Add(HeartbeatAsync(C ancelToken));
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
}
}
protected override Task Cleanup()
protected override Task Cleanup()
@@ -179,7 +178,7 @@ namespace Discord.Net.WebSockets
if (packetLength > 0 && endpoint.Equals(_endpoint))
if (packetLength > 0 && endpoint.Equals(_endpoint))
{
{
if (_s tate != ConnectionState.Connected)
if (S tate != ConnectionState.Connected)
{
{
if (packetLength != 70)
if (packetLength != 70)
return;
return;
@@ -235,7 +234,7 @@ namespace Discord.Net.WebSockets
ulong userId;
ulong userId;
if (_ssrcMapping.TryGetValue(ssrc, out userId))
if (_ssrcMapping.TryGetValue(ssrc, out userId))
RaiseOnPacket(userId, _c hannelId.Value, result, resultOffset, resultLength);
RaiseOnPacket(userId, C hannelId.Value, result, resultOffset, resultLength);
}
}
}
}
}
}
@@ -249,7 +248,7 @@ namespace Discord.Net.WebSockets
{
{
try
try
{
{
while (!cancelToken.IsCancellationRequested && _s tate != ConnectionState.Connected)
while (!cancelToken.IsCancellationRequested && S tate != ConnectionState.Connected)
Thread.Sleep(1);
Thread.Sleep(1);
if (cancelToken.IsCancellationRequested)
if (cancelToken.IsCancellationRequested)
@@ -353,7 +352,7 @@ namespace Discord.Net.WebSockets
}
}
catch (SocketException ex)
catch (SocketException ex)
{
{
_l ogger.Error("Failed to send UDP packet.", ex);
L ogger.Error("Failed to send UDP packet.", ex);
}
}
hasFrame = false;
hasFrame = false;
}
}
@@ -385,11 +384,7 @@ namespace Discord.Net.WebSockets
#if !DOTNET5_4
#if !DOTNET5_4
//Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken
//Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken
private Task WatcherAsync()
private Task WatcherAsync()
{
var cancelToken = _cancelToken;
return cancelToken.Wait()
.ContinueWith(_ => _udp.Close());
}
=> CancelToken.Wait().ContinueWith(_ => _udp.Close());
#endif
#endif
protected override async Task ProcessMessage(string json)
protected override async Task ProcessMessage(string json)
@@ -401,7 +396,7 @@ namespace Discord.Net.WebSockets
{
{
case OpCodes.Ready:
case OpCodes.Ready:
{
{
if (_s tate != ConnectionState.Connected)
if (S tate != ConnectionState.Connected)
{
{
var payload = (msg.Payload as JToken).ToObject<ReadyEvent>(_serializer);
var payload = (msg.Payload as JToken).ToObject<ReadyEvent>(_serializer);
_heartbeatInterval = payload.HeartbeatInterval;
_heartbeatInterval = payload.HeartbeatInterval;
@@ -460,24 +455,23 @@ namespace Discord.Net.WebSockets
}
}
break;
break;
default:
default:
if (_logger.Level >= LogSeverity.Warning)
_logger.Warning($"Unknown Opcode: {opCode}");
Logger.Warning($"Unknown Opcode: {opCode}");
break;
break;
}
}
}
}
public void SendPCMFrames(byte[] data, int bytes)
public void SendPCMFrames(byte[] data, int bytes)
{
{
_sendBuffer.Push(data, bytes, _c ancelToken);
_sendBuffer.Push(data, bytes, C ancelToken);
}
}
public void ClearPCMFrames()
public void ClearPCMFrames()
{
{
_sendBuffer.Clear(_c ancelToken);
_sendBuffer.Clear(C ancelToken);
}
}
public void WaitForQueue()
public void WaitForQueue()
{
{
_sendBuffer.Wait(_c ancelToken);
_sendBuffer.Wait(C ancelToken);
}
}
public Task WaitForConnection(int timeout)
public Task WaitForConnection(int timeout)
{
{
@@ -485,7 +479,7 @@ namespace Discord.Net.WebSockets
{
{
try
try
{
{
if (!_connectedEvent.Wait(timeout, _c ancelToken))
if (!_connectedEvent.Wait(timeout, C ancelToken))
throw new TimeoutException();
throw new TimeoutException();
}
}
catch (OperationCanceledException)
catch (OperationCanceledException)
@@ -498,9 +492,11 @@ namespace Discord.Net.WebSockets
public override void SendHeartbeat()
public override void SendHeartbeat()
=> QueueMessage(new HeartbeatCommand());
=> QueueMessage(new HeartbeatCommand());
public void SendIdentify()
public void SendIdentify()
=> QueueMessage(new IdentifyCommand { GuildId = _serverId.Value, UserId = _client.UserId.Value, SessionId = _client.SessionId, Token = _audioClient.Token });
=> QueueMessage(new IdentifyCommand { GuildId = ServerId.Value, UserId = _client.CurrentUser.Id,
SessionId = _client.SessionId, Token = Token });
public void SendSelectProtocol(string externalAddress, int externalPort)
public void SendSelectProtocol(string externalAddress, int externalPort)
=> QueueMessage(new SelectProtocolCommand { Protocol = "udp", ExternalAddress = externalAddress, ExternalPort = externalPort, EncryptionMode = _encryptionMode });
=> QueueMessage(new SelectProtocolCommand { Protocol = "udp", ExternalAddress = externalAddress,
ExternalPort = externalPort, EncryptionMode = _encryptionMode });
public void SendSetSpeaking(bool value)
public void SendSetSpeaking(bool value)
=> QueueMessage(new SetSpeakingCommand { IsSpeaking = value, Delay = 0 });
=> QueueMessage(new SetSpeakingCommand { IsSpeaking = value, Delay = 0 });