diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 628c8e032..85c2bf4e0 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -19,6 +19,9 @@ Net\DefaultWebSocketClient.cs + + ConnectionManager.cs + diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs index d3c50a5ec..2a9ae21bf 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs @@ -12,12 +12,12 @@ namespace Discord.Rpc remove { _connectedEvent.Remove(value); } } private readonly AsyncEvent> _connectedEvent = new AsyncEvent>(); - public event Func Disconnected + public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } - private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); public event Func Ready { add { _readyEvent.Add(value); } diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 5235c98d4..01d641204 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -40,6 +40,8 @@ namespace Discord.Rpc _rpcLogger = LogManager.CreateLogger("RPC"); _connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 5404227f2..30073baeb 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -59,28 +59,28 @@ namespace Discord.Audio internal AudioClient(SocketGuild guild, int id) { Guild = guild; + _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); + + ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); + ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); + ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); + //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); + ApiClient.ReceivedEvent += ProcessMessageAsync; + ApiClient.ReceivedPacket += ProcessPacketAsync; _stateLock = new SemaphoreSlim(1, 1); _connection = new ConnectionManager(_stateLock, _audioLogger, 30000, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _heartbeatTimes = new ConcurrentQueue(); - - _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { _audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); e.ErrorContext.Handled = true; - }; - - ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); - - ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); - ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); - //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); - ApiClient.ReceivedEvent += ProcessMessageAsync; - ApiClient.ReceivedPacket += ProcessPacketAsync; + }; LatencyUpdated += async (old, val) => await _audioLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); } @@ -98,25 +98,32 @@ namespace Discord.Audio private async Task OnConnectingAsync() { + await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false); + await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); + + //Wait for READY + await _connection.WaitAsync().ConfigureAwait(false); } private async Task OnDisconnectingAsync(Exception ex) { - //Disconnect from server + await _audioLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); await ApiClient.DisconnectAsync().ConfigureAwait(false); //Wait for tasks to complete + await _audioLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); var heartbeatTask = _heartbeatTask; if (heartbeatTask != null) await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; - await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); - long time; while (_heartbeatTimes.TryDequeue(out time)) { } _lastMessageTime = 0; + + await _audioLogger.DebugAsync("Sending Voice State").ConfigureAwait(false); + await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis) diff --git a/src/Discord.Net.Rest/ConnectionManager.cs b/src/Discord.Net.WebSocket/ConnectionManager.cs similarity index 95% rename from src/Discord.Net.Rest/ConnectionManager.cs rename to src/Discord.Net.WebSocket/ConnectionManager.cs index ab1f4790c..72926e2e3 100644 --- a/src/Discord.Net.Rest/ConnectionManager.cs +++ b/src/Discord.Net.WebSocket/ConnectionManager.cs @@ -2,6 +2,7 @@ using Discord.Logging; using System; using System.Threading; using System.Threading.Tasks; +using Discord.Net; namespace Discord { @@ -39,7 +40,13 @@ namespace Discord clientDisconnectHandler(ex => { if (ex != null) - Error(new Exception("WebSocket connection was closed", ex)); + { + var ex2 = ex as WebSocketClosedException; + if (ex2?.CloseCode == 4006) + CriticalError(new Exception("WebSocket session expired", ex)); + else + Error(new Exception("WebSocket connection was closed", ex)); + } else Error(new Exception("WebSocket connection was closed")); return Task.Delay(0); @@ -50,7 +57,7 @@ namespace Discord { await AcquireConnectionLock().ConfigureAwait(false); var reconnectCancelToken = new CancellationTokenSource(); - _reconnectCancelToken = new CancellationTokenSource(); + _reconnectCancelToken = reconnectCancelToken; _task = Task.Run(async () => { try diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 092225376..d52de0af1 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -95,6 +95,8 @@ namespace Discord.WebSocket _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _nextAudioId = 1; _connectionGroupLock = groupLock; @@ -173,8 +175,6 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); await ApiClient.ConnectAsync().ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); if (_sessionId != null) { @@ -189,7 +189,7 @@ namespace Discord.WebSocket //Wait for READY await _connection.WaitAsync().ConfigureAwait(false); - + await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); await SendStatusAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 4e16985a7..007f52124 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -506,15 +506,16 @@ namespace Discord.WebSocket } internal async Task FinishConnectAudio(int id, string url, string token) { + //TODO: Mem Leak: Disconnected/Connected handlers arent cleaned up var voiceState = GetVoiceState(Discord.CurrentUser.Id).Value; await _audioLock.WaitAsync().ConfigureAwait(false); try { + var promise = _audioConnectPromise; if (_audioClient == null) { var audioClient = new AudioClient(this, id); - var promise = _audioConnectPromise; audioClient.Disconnected += async ex => { if (!promise.Task.IsCompleted) @@ -532,7 +533,7 @@ namespace Discord.WebSocket } _audioClient.Connected += () => { - var _ = _audioConnectPromise.TrySetResultAsync(_audioClient); + var _ = promise.TrySetResultAsync(_audioClient); return Task.Delay(0); }; await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false);