Several socket and event bug fixes
This commit is contained in:
@@ -22,6 +22,12 @@ namespace Discord
|
|||||||
VoiceWebSocket,
|
VoiceWebSocket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DisconnectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public readonly bool WasUnexpected;
|
||||||
|
public readonly Exception Error;
|
||||||
|
internal DisconnectedEventArgs(bool wasUnexpected, Exception error) { WasUnexpected = wasUnexpected; Error = error; }
|
||||||
|
}
|
||||||
public sealed class LogMessageEventArgs : EventArgs
|
public sealed class LogMessageEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public LogMessageSeverity Severity { get; }
|
public LogMessageSeverity Severity { get; }
|
||||||
@@ -30,6 +36,7 @@ namespace Discord
|
|||||||
|
|
||||||
internal LogMessageEventArgs(LogMessageSeverity severity, LogMessageSource source, string msg) { Severity = severity; Source = source; Message = msg; }
|
internal LogMessageEventArgs(LogMessageSeverity severity, LogMessageSource source, string msg) { Severity = severity; Source = source; Message = msg; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ServerEventArgs : EventArgs
|
public sealed class ServerEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public Server Server { get; }
|
public Server Server { get; }
|
||||||
@@ -136,11 +143,11 @@ namespace Discord
|
|||||||
if (Connected != null)
|
if (Connected != null)
|
||||||
Connected(this, EventArgs.Empty);
|
Connected(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
public event EventHandler Disconnected;
|
public event EventHandler<DisconnectedEventArgs> Disconnected;
|
||||||
private void RaiseDisconnected()
|
private void RaiseDisconnected(DisconnectedEventArgs e)
|
||||||
{
|
{
|
||||||
if (Disconnected != null)
|
if (Disconnected != null)
|
||||||
Disconnected(this, EventArgs.Empty);
|
Disconnected(this, e);
|
||||||
}
|
}
|
||||||
public event EventHandler<LogMessageEventArgs> LogMessage;
|
public event EventHandler<LogMessageEventArgs> LogMessage;
|
||||||
internal void RaiseOnLog(LogMessageSeverity severity, LogMessageSource source, string message)
|
internal void RaiseOnLog(LogMessageSeverity severity, LogMessageSource source, string message)
|
||||||
@@ -308,11 +315,11 @@ namespace Discord
|
|||||||
if (VoiceConnected != null)
|
if (VoiceConnected != null)
|
||||||
VoiceConnected(this, EventArgs.Empty);
|
VoiceConnected(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
public event EventHandler VoiceDisconnected;
|
public event EventHandler<DisconnectedEventArgs> VoiceDisconnected;
|
||||||
private void RaiseVoiceDisconnected()
|
private void RaiseVoiceDisconnected(DisconnectedEventArgs e)
|
||||||
{
|
{
|
||||||
if (VoiceDisconnected != null)
|
if (VoiceDisconnected != null)
|
||||||
VoiceDisconnected(this, EventArgs.Empty);
|
VoiceDisconnected(this, e);
|
||||||
}
|
}
|
||||||
/*public event EventHandler<VoiceServerUpdatedEventArgs> VoiceServerChanged;
|
/*public event EventHandler<VoiceServerUpdatedEventArgs> VoiceServerChanged;
|
||||||
private void RaiseVoiceServerUpdated(Server server, string endpoint)
|
private void RaiseVoiceServerUpdated(Server server, string endpoint)
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ namespace Discord
|
|||||||
public void SendVoicePCM(byte[] data, int count)
|
public void SendVoicePCM(byte[] data, int count)
|
||||||
{
|
{
|
||||||
CheckReady(checkVoice: true);
|
CheckReady(checkVoice: true);
|
||||||
|
if (data == null) throw new ArgumentException(nameof(data));
|
||||||
|
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||||
if (count == 0) return;
|
if (count == 0) return;
|
||||||
|
|
||||||
_voiceSocket.SendPCMFrames(data, count);
|
_voiceSocket.SendPCMFrames(data, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ namespace Discord
|
|||||||
public void ClearVoicePCM()
|
public void ClearVoicePCM()
|
||||||
{
|
{
|
||||||
CheckReady(checkVoice: true);
|
CheckReady(checkVoice: true);
|
||||||
|
|
||||||
_voiceSocket.ClearPCMFrames();
|
_voiceSocket.ClearPCMFrames();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +57,7 @@ namespace Discord
|
|||||||
public async Task WaitVoice()
|
public async Task WaitVoice()
|
||||||
{
|
{
|
||||||
CheckReady(checkVoice: true);
|
CheckReady(checkVoice: true);
|
||||||
|
|
||||||
_voiceSocket.Wait();
|
_voiceSocket.Wait();
|
||||||
await TaskHelper.CompletedTask.ConfigureAwait(false);
|
await TaskHelper.CompletedTask.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Discord
|
|||||||
private Task _runTask;
|
private Task _runTask;
|
||||||
protected ExceptionDispatchInfo _disconnectReason;
|
protected ExceptionDispatchInfo _disconnectReason;
|
||||||
private bool _wasDisconnectUnexpected;
|
private bool _wasDisconnectUnexpected;
|
||||||
|
private string _token;
|
||||||
|
|
||||||
/// <summary> Returns the id of the current logged-in user. </summary>
|
/// <summary> Returns the id of the current logged-in user. </summary>
|
||||||
public string CurrentUserId => _currentUserId;
|
public string CurrentUserId => _currentUserId;
|
||||||
@@ -86,6 +87,7 @@ namespace Discord
|
|||||||
_config.Lock();
|
_config.Lock();
|
||||||
|
|
||||||
_state = (int)DiscordClientState.Disconnected;
|
_state = (int)DiscordClientState.Disconnected;
|
||||||
|
_cancelToken = new CancellationToken(true);
|
||||||
_disconnectedEvent = new ManualResetEvent(true);
|
_disconnectedEvent = new ManualResetEvent(true);
|
||||||
_connectedEvent = new ManualResetEventSlim(false);
|
_connectedEvent = new ManualResetEventSlim(false);
|
||||||
_rand = new Random();
|
_rand = new Random();
|
||||||
@@ -93,8 +95,13 @@ namespace Discord
|
|||||||
_api = new DiscordAPIClient(_config.LogLevel);
|
_api = new DiscordAPIClient(_config.LogLevel);
|
||||||
_dataSocket = new DataWebSocket(this);
|
_dataSocket = new DataWebSocket(this);
|
||||||
_dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); };
|
_dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); };
|
||||||
|
_dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Connect(_token); /*await _dataSocket.Reconnect(_cancelToken);*/ };
|
||||||
if (_config.EnableVoice)
|
if (_config.EnableVoice)
|
||||||
|
{
|
||||||
_voiceSocket = new VoiceWebSocket(this);
|
_voiceSocket = new VoiceWebSocket(this);
|
||||||
|
_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
|
||||||
|
_voiceSocket.Disconnected += async (s, e) => { RaiseVoiceDisconnected(e); if (e.WasUnexpected) await _voiceSocket.Reconnect(_cancelToken); };
|
||||||
|
}
|
||||||
|
|
||||||
_channels = new Channels(this);
|
_channels = new Channels(this);
|
||||||
_members = new Members(this);
|
_members = new Members(this);
|
||||||
@@ -108,10 +115,6 @@ namespace Discord
|
|||||||
_voiceSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message);
|
_voiceSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message);
|
||||||
if (_config.LogLevel >= LogMessageSeverity.Info)
|
if (_config.LogLevel >= LogMessageSeverity.Info)
|
||||||
{
|
{
|
||||||
Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, "Connected");
|
|
||||||
Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, "Disconnected");
|
|
||||||
VoiceConnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Voice Connected");
|
|
||||||
VoiceDisconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, $"Voice Disconnected");
|
|
||||||
_dataSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected");
|
_dataSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected");
|
||||||
_dataSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected");
|
_dataSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected");
|
||||||
//_dataSocket.ReceivedEvent += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, $"Received {e.Type}");
|
//_dataSocket.ReceivedEvent += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, $"Received {e.Type}");
|
||||||
@@ -604,6 +607,7 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
|
|
||||||
//_state = (int)DiscordClientState.Connected;
|
//_state = (int)DiscordClientState.Connected;
|
||||||
|
_token = token;
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -616,23 +620,24 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
_state = (int)WebSocketState.Connected;
|
_state = (int)WebSocketState.Connected;
|
||||||
_connectedEvent.Set();
|
_connectedEvent.Set();
|
||||||
|
RaiseConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Disconnects from the Discord server, canceling any pending requests. </summary>
|
/// <summary> Disconnects from the Discord server, canceling any pending requests. </summary>
|
||||||
public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
|
public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
|
||||||
protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
|
protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
|
||||||
{
|
{
|
||||||
int oldState;
|
int oldState;
|
||||||
bool hasWriterLock;
|
bool hasWriterLock;
|
||||||
|
|
||||||
//If in either connecting or connected state, get a lock by being the first to switch to disconnecting
|
//If in either connecting or connected state, get a lock by being the first to switch to disconnecting
|
||||||
oldState = Interlocked.CompareExchange(ref _state, (int)DiscordClientState.Disconnecting, (int)DiscordClientState.Connecting);
|
oldState = Interlocked.CompareExchange(ref _state, (int)DiscordClientState.Disconnecting, (int)DiscordClientState.Connecting);
|
||||||
if (oldState == (int)DiscordClientState.Disconnected) return; //Already disconnected
|
if (oldState == (int)DiscordClientState.Disconnected) return TaskHelper.CompletedTask; //Already disconnected
|
||||||
hasWriterLock = oldState == (int)DiscordClientState.Connecting; //Caused state change
|
hasWriterLock = oldState == (int)DiscordClientState.Connecting; //Caused state change
|
||||||
if (!hasWriterLock)
|
if (!hasWriterLock)
|
||||||
{
|
{
|
||||||
oldState = Interlocked.CompareExchange(ref _state, (int)DiscordClientState.Disconnecting, (int)DiscordClientState.Connected);
|
oldState = Interlocked.CompareExchange(ref _state, (int)DiscordClientState.Disconnecting, (int)DiscordClientState.Connected);
|
||||||
if (oldState == (int)DiscordClientState.Disconnected) return; //Already disconnected
|
if (oldState == (int)DiscordClientState.Disconnected) return TaskHelper.CompletedTask; //Already disconnected
|
||||||
hasWriterLock = oldState == (int)DiscordClientState.Connected; //Caused state change
|
hasWriterLock = oldState == (int)DiscordClientState.Connected; //Caused state change
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,18 +649,9 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skipAwait)
|
if (!skipAwait)
|
||||||
{
|
return _runTask ?? TaskHelper.CompletedTask;
|
||||||
Task task = _runTask;
|
else
|
||||||
if (task != null)
|
return TaskHelper.CompletedTask;
|
||||||
await task.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasWriterLock)
|
|
||||||
{
|
|
||||||
_state = (int)DiscordClientState.Disconnected;
|
|
||||||
_disconnectedEvent.Set();
|
|
||||||
_connectedEvent.Reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunTasks()
|
private async Task RunTasks()
|
||||||
@@ -672,13 +668,14 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); }
|
catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); }
|
||||||
|
|
||||||
await Cleanup().ConfigureAwait(false);
|
bool wasUnexpected = _wasDisconnectUnexpected;
|
||||||
|
_wasDisconnectUnexpected = false;
|
||||||
|
|
||||||
|
await Cleanup(wasUnexpected).ConfigureAwait(false);
|
||||||
_runTask = null;
|
_runTask = null;
|
||||||
}
|
}
|
||||||
private async Task Cleanup()
|
private async Task Cleanup(bool wasUnexpected)
|
||||||
{
|
{
|
||||||
_disconnectedEvent.Set();
|
|
||||||
|
|
||||||
await _dataSocket.Disconnect().ConfigureAwait(false);
|
await _dataSocket.Disconnect().ConfigureAwait(false);
|
||||||
if (_config.EnableVoice)
|
if (_config.EnableVoice)
|
||||||
await _voiceSocket.Disconnect().ConfigureAwait(false);
|
await _voiceSocket.Disconnect().ConfigureAwait(false);
|
||||||
@@ -695,6 +692,14 @@ namespace Discord
|
|||||||
|
|
||||||
_currentUser = null;
|
_currentUser = null;
|
||||||
_currentUserId = null;
|
_currentUserId = null;
|
||||||
|
_token = null;
|
||||||
|
|
||||||
|
if (!wasUnexpected)
|
||||||
|
{
|
||||||
|
_state = (int)DiscordClientState.Disconnected;
|
||||||
|
_disconnectedEvent.Set();
|
||||||
|
}
|
||||||
|
_connectedEvent.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Helpers
|
//Helpers
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
internal partial class VoiceWebSocket : WebSocket
|
internal partial class VoiceWebSocket : WebSocket
|
||||||
{
|
{
|
||||||
private readonly int _targetAudioBufferLength;
|
private const string EncryptedMode = "xsalsa20_poly1305";
|
||||||
|
private const string UnencryptedMode = "plain";
|
||||||
|
|
||||||
|
private readonly int _targetAudioBufferLength;
|
||||||
private ManualResetEventSlim _connectWaitOnLogin;
|
private ManualResetEventSlim _connectWaitOnLogin;
|
||||||
private uint _ssrc;
|
private uint _ssrc;
|
||||||
private readonly Random _rand = new Random();
|
private readonly Random _rand = new Random();
|
||||||
@@ -26,11 +29,9 @@ namespace Discord.Net.WebSockets
|
|||||||
private ManualResetEventSlim _sendQueueWait, _sendQueueEmptyWait;
|
private ManualResetEventSlim _sendQueueWait, _sendQueueEmptyWait;
|
||||||
private UdpClient _udp;
|
private UdpClient _udp;
|
||||||
private IPEndPoint _endpoint;
|
private IPEndPoint _endpoint;
|
||||||
private bool _isReady, _isClearing;
|
private bool _isClearing, _isEncrypted;
|
||||||
private byte[] _secretKey;
|
private byte[] _secretKey;
|
||||||
private string _myIp;
|
|
||||||
private ushort _sequence;
|
private ushort _sequence;
|
||||||
private string _mode;
|
|
||||||
private byte[] _encodingBuffer;
|
private byte[] _encodingBuffer;
|
||||||
private string _serverId, _userId, _sessionId, _token;
|
private string _serverId, _userId, _sessionId, _token;
|
||||||
|
|
||||||
@@ -52,24 +53,32 @@ namespace Discord.Net.WebSockets
|
|||||||
_targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames
|
_targetAudioBufferLength = client.Config.VoiceBufferLength / 20; //20 ms frames
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Login(string host, string serverId, string userId, string sessionId, string token, CancellationToken cancelToken)
|
public async Task Login(string host, string serverId, string userId, string sessionId, string token, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
|
if (_serverId == serverId && _userId == userId && _sessionId == sessionId && _token == token)
|
||||||
|
{
|
||||||
|
//Adjust the host and tell the system to reconnect
|
||||||
|
_host = host;
|
||||||
|
await DisconnectInternal(new Exception("Server transfer occurred."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_serverId = serverId;
|
_serverId = serverId;
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
_sessionId = sessionId;
|
_sessionId = sessionId;
|
||||||
_token = token;
|
_token = token;
|
||||||
|
|
||||||
return base.Connect(host, cancelToken);
|
await Connect(host, cancelToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task[] Run()
|
protected override Task[] Run()
|
||||||
{
|
{
|
||||||
|
_isClearing = false;
|
||||||
|
|
||||||
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
|
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
|
||||||
#if !DNX451
|
#if !DNX451
|
||||||
_udp.AllowNatTraversal(true);
|
_udp.AllowNatTraversal(true);
|
||||||
#endif
|
#endif
|
||||||
_isReady = false;
|
|
||||||
_isClearing = false;
|
|
||||||
|
|
||||||
VoiceCommands.Login msg = new VoiceCommands.Login();
|
VoiceCommands.Login msg = new VoiceCommands.Login();
|
||||||
msg.Payload.ServerId = _serverId;
|
msg.Payload.ServerId = _serverId;
|
||||||
@@ -93,19 +102,24 @@ namespace Discord.Net.WebSockets
|
|||||||
#endif
|
#endif
|
||||||
}.Concat(base.Run()).ToArray();
|
}.Concat(base.Run()).ToArray();
|
||||||
}
|
}
|
||||||
protected override Task Cleanup()
|
protected override Task Cleanup(bool wasUnexpected)
|
||||||
{
|
{
|
||||||
ClearPCMFrames();
|
|
||||||
_udp = null;
|
|
||||||
_serverId = null;
|
|
||||||
_userId = null;
|
|
||||||
_sessionId = null;
|
|
||||||
_token = null;
|
|
||||||
#if USE_THREAD
|
#if USE_THREAD
|
||||||
_sendThread.Join();
|
_sendThread.Join();
|
||||||
_sendThread = null;
|
_sendThread = null;
|
||||||
#endif
|
#endif
|
||||||
return base.Cleanup();
|
|
||||||
|
ClearPCMFrames();
|
||||||
|
if (!wasUnexpected)
|
||||||
|
{
|
||||||
|
_serverId = null;
|
||||||
|
_userId = null;
|
||||||
|
_sessionId = null;
|
||||||
|
_token = null;
|
||||||
|
}
|
||||||
|
_udp = null;
|
||||||
|
|
||||||
|
return base.Cleanup(wasUnexpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReceiveVoiceAsync()
|
private async Task ReceiveVoiceAsync()
|
||||||
@@ -153,14 +167,16 @@ namespace Discord.Net.WebSockets
|
|||||||
byte[] packet;
|
byte[] packet;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancelToken.IsCancellationRequested && !_isReady)
|
while (!cancelToken.IsCancellationRequested && _state != (int)WebSocketState.Connected)
|
||||||
|
{
|
||||||
#if USE_THREAD
|
#if USE_THREAD
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
#else
|
#else
|
||||||
await Task.Delay(1);
|
await Task.Delay(1);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (cancelToken.IsCancellationRequested)
|
if (cancelToken.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint timestamp = 0;
|
uint timestamp = 0;
|
||||||
@@ -251,15 +267,15 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
case 2: //READY
|
case 2: //READY
|
||||||
{
|
{
|
||||||
if (!_isReady)
|
if (_state != (int)WebSocketState.Connected)
|
||||||
{
|
{
|
||||||
var payload = (msg.Payload as JToken).ToObject<VoiceEvents.Ready>();
|
var payload = (msg.Payload as JToken).ToObject<VoiceEvents.Ready>();
|
||||||
_heartbeatInterval = payload.HeartbeatInterval;
|
_heartbeatInterval = payload.HeartbeatInterval;
|
||||||
_ssrc = payload.SSRC;
|
_ssrc = payload.SSRC;
|
||||||
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
|
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(_host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
|
||||||
//_mode = payload.Modes.LastOrDefault();
|
//_mode = payload.Modes.LastOrDefault();
|
||||||
_mode = "plain";
|
_isEncrypted = !payload.Modes.Contains("plain");
|
||||||
_udp.Connect(_endpoint);
|
_udp.Connect(_endpoint);
|
||||||
|
|
||||||
_sequence = (ushort)_rand.Next(0, ushort.MaxValue);
|
_sequence = (ushort)_rand.Next(0, ushort.MaxValue);
|
||||||
//No thread issue here because SendAsync doesn't start until _isReady is true
|
//No thread issue here because SendAsync doesn't start until _isReady is true
|
||||||
@@ -297,9 +313,8 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
byte[] buffer = msg.Buffer;
|
byte[] buffer = msg.Buffer;
|
||||||
int length = msg.Buffer.Length;
|
int length = msg.Buffer.Length;
|
||||||
if (!_isReady)
|
if (_state != (int)WebSocketState.Connected)
|
||||||
{
|
{
|
||||||
_isReady = true;
|
|
||||||
if (length != 70)
|
if (length != 70)
|
||||||
{
|
{
|
||||||
if (_logLevel >= LogMessageSeverity.Warning)
|
if (_logLevel >= LogMessageSeverity.Warning)
|
||||||
@@ -308,15 +323,15 @@ namespace Discord.Net.WebSockets
|
|||||||
}
|
}
|
||||||
|
|
||||||
int port = buffer[68] | buffer[69] << 8;
|
int port = buffer[68] | buffer[69] << 8;
|
||||||
|
string ip = Encoding.ASCII.GetString(buffer, 4, 70 - 6).TrimEnd('\0');
|
||||||
|
|
||||||
_myIp = Encoding.ASCII.GetString(buffer, 4, 70 - 6).TrimEnd('\0');
|
CompleteConnect();
|
||||||
|
|
||||||
_isReady = true;
|
|
||||||
var login2 = new VoiceCommands.Login2();
|
var login2 = new VoiceCommands.Login2();
|
||||||
login2.Payload.Protocol = "udp";
|
login2.Payload.Protocol = "udp";
|
||||||
login2.Payload.SocketData.Address = _myIp;
|
login2.Payload.SocketData.Address = ip;
|
||||||
login2.Payload.SocketData.Mode = _mode;
|
login2.Payload.SocketData.Mode = _isEncrypted ? EncryptedMode : UnencryptedMode;
|
||||||
login2.Payload.SocketData.Port = port;
|
login2.Payload.SocketData.Port = port;
|
||||||
QueueMessage(login2);
|
QueueMessage(login2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -377,8 +392,8 @@ namespace Discord.Net.WebSockets
|
|||||||
buffer = newBuffer;
|
buffer = newBuffer;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if (_logLevel >= LogMessageSeverity.Verbose)
|
if (_logLevel >= LogMessageSeverity.Debug)
|
||||||
RaiseOnLog(LogMessageSeverity.Verbose, $"Received {buffer.Length - 12} bytes.");
|
RaiseOnLog(LogMessageSeverity.Debug, $"Received {buffer.Length - 12} bytes.");
|
||||||
//TODO: Use Voice Data
|
//TODO: Use Voice Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,12 +401,6 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
public void SendPCMFrames(byte[] data, int bytes)
|
public void SendPCMFrames(byte[] data, int bytes)
|
||||||
{
|
{
|
||||||
var cancelToken = _cancelToken;
|
|
||||||
if (!_isReady || cancelToken == null)
|
|
||||||
throw new InvalidOperationException("Not connected to a voice server.");
|
|
||||||
if (bytes == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int frameSize = _encoder.FrameSize;
|
int frameSize = _encoder.FrameSize;
|
||||||
int frames = bytes / frameSize;
|
int frames = bytes / frameSize;
|
||||||
int expectedBytes = frames * frameSize;
|
int expectedBytes = frames * frameSize;
|
||||||
@@ -431,7 +440,7 @@ namespace Discord.Net.WebSockets
|
|||||||
int encodedLength = _encoder.EncodeFrame(data, pos, _encodingBuffer);
|
int encodedLength = _encoder.EncodeFrame(data, pos, _encodingBuffer);
|
||||||
|
|
||||||
//TODO: Handle Encryption
|
//TODO: Handle Encryption
|
||||||
if (_mode == "xsalsa20_poly1305")
|
if (_isEncrypted)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,16 +457,16 @@ namespace Discord.Net.WebSockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_logLevel >= LogMessageSeverity.Verbose)
|
if (_logLevel >= LogMessageSeverity.Debug)
|
||||||
RaiseOnLog(LogMessageSeverity.Verbose, $"Queued {bytes} bytes for voice output.");
|
RaiseOnLog(LogMessageSeverity.Debug, $"Queued {bytes} bytes for voice output.");
|
||||||
}
|
}
|
||||||
public void ClearPCMFrames()
|
public void ClearPCMFrames()
|
||||||
{
|
{
|
||||||
_isClearing = true;
|
_isClearing = true;
|
||||||
byte[] ignored;
|
byte[] ignored;
|
||||||
while (_sendQueue.TryDequeue(out ignored)) { }
|
while (_sendQueue.TryDequeue(out ignored)) { }
|
||||||
if (_logLevel >= LogMessageSeverity.Verbose)
|
if (_logLevel >= LogMessageSeverity.Debug)
|
||||||
RaiseOnLog(LogMessageSeverity.Verbose, "Cleared the voice buffer.");
|
RaiseOnLog(LogMessageSeverity.Debug, "Cleared the voice buffer.");
|
||||||
_isClearing = false;
|
_isClearing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,6 @@
|
|||||||
|
|
||||||
namespace Discord.Net.WebSockets
|
namespace Discord.Net.WebSockets
|
||||||
{
|
{
|
||||||
public class DisconnectedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public readonly bool WasUnexpected;
|
|
||||||
public readonly Exception Error;
|
|
||||||
internal DisconnectedEventArgs(bool wasUnexpected, Exception error) { WasUnexpected = wasUnexpected; Error = error; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal partial class WebSocket
|
internal partial class WebSocket
|
||||||
{
|
{
|
||||||
public event EventHandler Connected;
|
public event EventHandler Connected;
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ namespace Discord.Net.WebSockets
|
|||||||
protected readonly DiscordClient _client;
|
protected readonly DiscordClient _client;
|
||||||
protected readonly LogMessageSeverity _logLevel;
|
protected readonly LogMessageSeverity _logLevel;
|
||||||
|
|
||||||
protected int _state;
|
|
||||||
protected string _host;
|
protected string _host;
|
||||||
protected int _loginTimeout, _heartbeatInterval;
|
protected int _loginTimeout, _heartbeatInterval;
|
||||||
private DateTime _lastHeartbeat;
|
private DateTime _lastHeartbeat;
|
||||||
private Task _runTask;
|
private Task _runTask;
|
||||||
|
|
||||||
|
public WebSocketState State => (WebSocketState)_state;
|
||||||
|
protected int _state;
|
||||||
|
|
||||||
protected ExceptionDispatchInfo _disconnectReason;
|
protected ExceptionDispatchInfo _disconnectReason;
|
||||||
private bool _wasDisconnectUnexpected;
|
private bool _wasDisconnectUnexpected;
|
||||||
|
|
||||||
@@ -56,17 +58,45 @@ namespace Discord.Net.WebSockets
|
|||||||
_client = client;
|
_client = client;
|
||||||
_logLevel = client.Config.LogLevel;
|
_logLevel = client.Config.LogLevel;
|
||||||
_loginTimeout = client.Config.ConnectionTimeout;
|
_loginTimeout = client.Config.ConnectionTimeout;
|
||||||
|
_cancelToken = new CancellationToken(true);
|
||||||
|
|
||||||
_engine = new BuiltInWebSocketEngine(client.Config.WebSocketInterval);
|
_engine = new BuiltInWebSocketEngine(client.Config.WebSocketInterval);
|
||||||
_engine.ProcessMessage += (s, e) =>
|
_engine.ProcessMessage += async (s, e) =>
|
||||||
{
|
{
|
||||||
if (_logLevel >= LogMessageSeverity.Debug)
|
if (_logLevel >= LogMessageSeverity.Debug)
|
||||||
RaiseOnLog(LogMessageSeverity.Debug, $"In: " + e.Message);
|
RaiseOnLog(LogMessageSeverity.Debug, $"In: " + e.Message);
|
||||||
ProcessMessage(e.Message);
|
await ProcessMessage(e.Message);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Reconnect(CancellationToken cancelToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
|
||||||
|
while (!cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Connect(_host, cancelToken).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { throw; }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
RaiseOnLog(LogMessageSeverity.Error, $"DataSocket reconnect failed: {ex.GetBaseException().Message}");
|
||||||
|
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
|
||||||
|
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { }
|
||||||
|
}
|
||||||
protected virtual async Task Connect(string host, CancellationToken cancelToken)
|
protected virtual async Task Connect(string host, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
|
if (_state != (int)WebSocketState.Disconnected)
|
||||||
|
throw new InvalidOperationException("Client is already connected or connecting to the server.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Disconnect().ConfigureAwait(false);
|
await Disconnect().ConfigureAwait(false);
|
||||||
@@ -97,19 +127,19 @@ namespace Discord.Net.WebSockets
|
|||||||
=> Connect(_host, _cancelToken);*/
|
=> Connect(_host, _cancelToken);*/
|
||||||
|
|
||||||
public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
|
public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
|
||||||
protected async Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
|
protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
|
||||||
{
|
{
|
||||||
int oldState;
|
int oldState;
|
||||||
bool hasWriterLock;
|
bool hasWriterLock;
|
||||||
|
|
||||||
//If in either connecting or connected state, get a lock by being the first to switch to disconnecting
|
//If in either connecting or connected state, get a lock by being the first to switch to disconnecting
|
||||||
oldState = Interlocked.CompareExchange(ref _state, (int)WebSocketState.Disconnecting, (int)WebSocketState.Connecting);
|
oldState = Interlocked.CompareExchange(ref _state, (int)WebSocketState.Disconnecting, (int)WebSocketState.Connecting);
|
||||||
if (oldState == (int)WebSocketState.Disconnected) return; //Already disconnected
|
if (oldState == (int)WebSocketState.Disconnected) return TaskHelper.CompletedTask; //Already disconnected
|
||||||
hasWriterLock = oldState == (int)WebSocketState.Connecting; //Caused state change
|
hasWriterLock = oldState == (int)WebSocketState.Connecting; //Caused state change
|
||||||
if (!hasWriterLock)
|
if (!hasWriterLock)
|
||||||
{
|
{
|
||||||
oldState = Interlocked.CompareExchange(ref _state, (int)WebSocketState.Disconnecting, (int)WebSocketState.Connected);
|
oldState = Interlocked.CompareExchange(ref _state, (int)WebSocketState.Disconnecting, (int)WebSocketState.Connected);
|
||||||
if (oldState == (int)WebSocketState.Disconnected) return; //Already disconnected
|
if (oldState == (int)WebSocketState.Disconnected) return TaskHelper.CompletedTask; //Already disconnected
|
||||||
hasWriterLock = oldState == (int)WebSocketState.Connected; //Caused state change
|
hasWriterLock = oldState == (int)WebSocketState.Connected; //Caused state change
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,17 +151,9 @@ namespace Discord.Net.WebSockets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skipAwait)
|
if (!skipAwait)
|
||||||
{
|
return _runTask ?? TaskHelper.CompletedTask;
|
||||||
Task task = _runTask;
|
else
|
||||||
if (task != null)
|
return TaskHelper.CompletedTask;
|
||||||
await task.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasWriterLock)
|
|
||||||
{
|
|
||||||
_state = (int)WebSocketState.Disconnected;
|
|
||||||
RaiseDisconnected(isUnexpected, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task RunTasks()
|
protected virtual async Task RunTasks()
|
||||||
@@ -146,9 +168,8 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
bool wasUnexpected = _wasDisconnectUnexpected;
|
bool wasUnexpected = _wasDisconnectUnexpected;
|
||||||
_wasDisconnectUnexpected = false;
|
_wasDisconnectUnexpected = false;
|
||||||
|
|
||||||
await _engine.Disconnect().ConfigureAwait(false);
|
await Cleanup(wasUnexpected).ConfigureAwait(false);
|
||||||
await Cleanup().ConfigureAwait(false);
|
|
||||||
_runTask = null;
|
_runTask = null;
|
||||||
}
|
}
|
||||||
protected virtual Task[] Run()
|
protected virtual Task[] Run()
|
||||||
@@ -158,10 +179,12 @@ namespace Discord.Net.WebSockets
|
|||||||
.Concat(new Task[] { HeartbeatAsync(cancelToken) })
|
.Concat(new Task[] { HeartbeatAsync(cancelToken) })
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
protected virtual Task Cleanup()
|
protected virtual Task Cleanup(bool wasUnexpected)
|
||||||
{
|
{
|
||||||
_cancelTokenSource = null;
|
_cancelTokenSource = null;
|
||||||
return TaskHelper.CompletedTask;
|
_state = (int)WebSocketState.Disconnected;
|
||||||
|
RaiseDisconnected(wasUnexpected, _disconnectReason?.SourceException);
|
||||||
|
return _engine.Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task ProcessMessage(string json);
|
protected abstract Task ProcessMessage(string json);
|
||||||
|
|||||||
Reference in New Issue
Block a user