Added VOICE_SERVER_UPDATE handling
This commit is contained in:
@@ -28,24 +28,19 @@ namespace Discord.Audio
|
||||
private readonly AsyncEvent<Func<VoiceOpCode, object, Task>> _receivedEvent = new AsyncEvent<Func<VoiceOpCode, object, Task>>();
|
||||
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
|
||||
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
|
||||
|
||||
private readonly ulong _userId;
|
||||
private readonly string _token;
|
||||
|
||||
private readonly JsonSerializer _serializer;
|
||||
private readonly IWebSocketClient _gatewayClient;
|
||||
private readonly SemaphoreSlim _connectionLock;
|
||||
private CancellationTokenSource _connectCancelToken;
|
||||
private bool _isDisposed;
|
||||
|
||||
public ulong GuildId { get; }
|
||||
public string SessionId { get; }
|
||||
public ulong GuildId { get; }
|
||||
public ConnectionState ConnectionState { get; private set; }
|
||||
|
||||
internal DiscordVoiceAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
|
||||
internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
|
||||
{
|
||||
GuildId = guildId;
|
||||
_userId = userId;
|
||||
SessionId = sessionId;
|
||||
_token = token;
|
||||
_connectionLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
_gatewayClient = webSocketProvider();
|
||||
@@ -78,6 +73,19 @@ namespace Discord.Audio
|
||||
|
||||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
|
||||
}
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_connectCancelToken?.Dispose();
|
||||
(_gatewayClient as IDisposable)?.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
public Task SendAsync(VoiceOpCode opCode, object payload, RequestOptions options = null)
|
||||
{
|
||||
@@ -105,16 +113,16 @@ namespace Discord.Audio
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ConnectAsync(string url)
|
||||
public async Task ConnectAsync(string url, ulong userId, string sessionId, string token)
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await ConnectInternalAsync(url).ConfigureAwait(false);
|
||||
await ConnectInternalAsync(url, userId, sessionId, token).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task ConnectInternalAsync(string url)
|
||||
private async Task ConnectInternalAsync(string url, ulong userId, string sessionId, string token)
|
||||
{
|
||||
ConnectionState = ConnectionState.Connecting;
|
||||
try
|
||||
@@ -123,7 +131,7 @@ namespace Discord.Audio
|
||||
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
|
||||
await _gatewayClient.ConnectAsync(url).ConfigureAwait(false);
|
||||
|
||||
await SendIdentityAsync(GuildId, _userId, SessionId, _token).ConfigureAwait(false);
|
||||
await SendIdentityAsync(GuildId, userId, sessionId, token).ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Connected;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Discord.API.Voice;
|
||||
using Discord.Logging;
|
||||
using Discord.Net.Converters;
|
||||
using Discord.Net.WebSockets;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading;
|
||||
@@ -9,7 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Audio
|
||||
{
|
||||
internal class AudioClient : IAudioClient
|
||||
internal class AudioClient : IAudioClient, IDisposable
|
||||
{
|
||||
public event Func<Task> Connected
|
||||
{
|
||||
@@ -17,12 +16,12 @@ namespace Discord.Audio
|
||||
remove { _connectedEvent.Remove(value); }
|
||||
}
|
||||
private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>();
|
||||
public event Func<Task> Disconnected
|
||||
public event Func<Exception, Task> Disconnected
|
||||
{
|
||||
add { _disconnectedEvent.Add(value); }
|
||||
remove { _disconnectedEvent.Remove(value); }
|
||||
}
|
||||
private readonly AsyncEvent<Func<Task>> _disconnectedEvent = new AsyncEvent<Func<Task>>();
|
||||
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
|
||||
public event Func<int, int, Task> LatencyUpdated
|
||||
{
|
||||
add { _latencyUpdatedEvent.Add(value); }
|
||||
@@ -34,28 +33,30 @@ namespace Discord.Audio
|
||||
#if BENCHMARK
|
||||
private readonly ILogger _benchmarkLogger;
|
||||
#endif
|
||||
private readonly JsonSerializer _serializer;
|
||||
internal readonly SemaphoreSlim _connectionLock;
|
||||
private readonly JsonSerializer _serializer;
|
||||
|
||||
private TaskCompletionSource<bool> _connectTask;
|
||||
private CancellationTokenSource _cancelToken;
|
||||
private Task _heartbeatTask, _reconnectTask;
|
||||
private Task _heartbeatTask;
|
||||
private long _heartbeatTime;
|
||||
private bool _isReconnecting;
|
||||
private string _url;
|
||||
private bool _isDisposed;
|
||||
|
||||
private DiscordSocketClient Discord { get; }
|
||||
public CachedGuild Guild { get; }
|
||||
public DiscordVoiceAPIClient ApiClient { get; private set; }
|
||||
public ConnectionState ConnectionState { get; private set; }
|
||||
public int Latency { get; private set; }
|
||||
|
||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||
internal AudioClient(DiscordSocketClient discord, ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, ILogManager logManager)
|
||||
{
|
||||
Discord = discord;
|
||||
private DiscordSocketClient Discord => Guild.Discord;
|
||||
|
||||
_webSocketLogger = logManager.CreateLogger("Audio");
|
||||
_udpLogger = logManager.CreateLogger("AudioUDP");
|
||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||
internal AudioClient(CachedGuild guild)
|
||||
{
|
||||
Guild = guild;
|
||||
|
||||
_webSocketLogger = Discord.LogManager.CreateLogger("Audio");
|
||||
_udpLogger = Discord.LogManager.CreateLogger("AudioUDP");
|
||||
#if BENCHMARK
|
||||
_benchmarkLogger = logManager.CreateLogger("Benchmark");
|
||||
#endif
|
||||
@@ -69,38 +70,34 @@ namespace Discord.Audio
|
||||
e.ErrorContext.Handled = true;
|
||||
};
|
||||
|
||||
ApiClient = new DiscordVoiceAPIClient(guildId, userId, sessionId, token, webSocketProvider);
|
||||
ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider);
|
||||
|
||||
ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false);
|
||||
ApiClient.ReceivedEvent += ProcessMessageAsync;
|
||||
ApiClient.Disconnected += async ex =>
|
||||
{
|
||||
if (ex != null)
|
||||
{
|
||||
await _webSocketLogger.WarningAsync($"Connection Closed: {ex.Message}").ConfigureAwait(false);
|
||||
await StartReconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await _webSocketLogger.WarningAsync($"Connection Closed").ConfigureAwait(false);
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ConnectAsync(string url)
|
||||
public async Task ConnectAsync(string url, ulong userId, string sessionId, string token)
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_isReconnecting = false;
|
||||
await ConnectInternalAsync(url).ConfigureAwait(false);
|
||||
await ConnectInternalAsync(url, userId, sessionId, token).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task ConnectInternalAsync(string url)
|
||||
private async Task ConnectInternalAsync(string url, ulong userId, string sessionId, string token)
|
||||
{
|
||||
var state = ConnectionState;
|
||||
if (state == ConnectionState.Connecting || state == ConnectionState.Connected)
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Connecting;
|
||||
await _webSocketLogger.InfoAsync("Connecting").ConfigureAwait(false);
|
||||
@@ -109,7 +106,7 @@ namespace Discord.Audio
|
||||
_url = url;
|
||||
_connectTask = new TaskCompletionSource<bool>();
|
||||
_cancelToken = new CancellationTokenSource();
|
||||
await ApiClient.ConnectAsync(url).ConfigureAwait(false);
|
||||
await ApiClient.ConnectAsync(url, userId, sessionId, token).ConfigureAwait(false);
|
||||
await _connectedEvent.InvokeAsync().ConfigureAwait(false);
|
||||
|
||||
await _connectTask.Task.ConfigureAwait(false);
|
||||
@@ -119,7 +116,7 @@ namespace Discord.Audio
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -129,12 +126,20 @@ namespace Discord.Audio
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_isReconnecting = false;
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task DisconnectInternalAsync()
|
||||
private async Task DisconnectAsync(Exception ex)
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DisconnectInternalAsync(ex).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task DisconnectInternalAsync(Exception ex)
|
||||
{
|
||||
if (ConnectionState == ConnectionState.Disconnected) return;
|
||||
ConnectionState = ConnectionState.Disconnecting;
|
||||
@@ -155,61 +160,7 @@ namespace Discord.Audio
|
||||
ConnectionState = ConnectionState.Disconnected;
|
||||
await _webSocketLogger.InfoAsync("Disconnected").ConfigureAwait(false);
|
||||
|
||||
await _disconnectedEvent.InvokeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task StartReconnectAsync()
|
||||
{
|
||||
//TODO: Is this thread-safe?
|
||||
if (_reconnectTask != null) return;
|
||||
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (_reconnectTask != null) return;
|
||||
_isReconnecting = true;
|
||||
_reconnectTask = ReconnectInternalAsync();
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task ReconnectInternalAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
int nextReconnectDelay = 1000;
|
||||
while (_isReconnecting)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(nextReconnectDelay).ConfigureAwait(false);
|
||||
nextReconnectDelay *= 2;
|
||||
if (nextReconnectDelay > 30000)
|
||||
nextReconnectDelay = 30000;
|
||||
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await ConnectInternalAsync(_url).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await _webSocketLogger.WarningAsync("Reconnect failed", ex).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
_isReconnecting = false;
|
||||
_reconnectTask = null;
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload)
|
||||
@@ -285,7 +236,7 @@ namespace Discord.Audio
|
||||
if (ConnectionState == ConnectionState.Connected)
|
||||
{
|
||||
await _webSocketLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false);
|
||||
await StartReconnectAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -296,5 +247,14 @@ namespace Discord.Audio
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
internal virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
_isDisposed = true;
|
||||
ApiClient.Dispose();
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Discord.Audio
|
||||
public interface IAudioClient
|
||||
{
|
||||
event Func<Task> Connected;
|
||||
event Func<Task> Disconnected;
|
||||
event Func<Exception, Task> Disconnected;
|
||||
event Func<int, int, Task> LatencyUpdated;
|
||||
|
||||
DiscordVoiceAPIClient ApiClient { get; }
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace Discord
|
||||
|
||||
internal readonly ILogger _discordLogger, _restLogger, _queueLogger;
|
||||
internal readonly SemaphoreSlim _connectionLock;
|
||||
internal readonly LogManager _log;
|
||||
internal readonly RequestQueue _requestQueue;
|
||||
internal bool _isDisposed;
|
||||
internal SelfUser _currentUser;
|
||||
|
||||
public API.DiscordApiClient ApiClient { get; }
|
||||
internal LogManager LogManager { get; }
|
||||
public LoginState LoginState { get; private set; }
|
||||
public API.DiscordApiClient ApiClient { get; private set; }
|
||||
|
||||
/// <summary> Creates a new REST-only discord client. </summary>
|
||||
public DiscordClient()
|
||||
@@ -40,11 +40,11 @@ namespace Discord
|
||||
/// <summary> Creates a new REST-only discord client. </summary>
|
||||
public DiscordClient(DiscordConfig config)
|
||||
{
|
||||
_log = new LogManager(config.LogLevel);
|
||||
_log.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
|
||||
_discordLogger = _log.CreateLogger("Discord");
|
||||
_restLogger = _log.CreateLogger("Rest");
|
||||
_queueLogger = _log.CreateLogger("Queue");
|
||||
LogManager = new LogManager(config.LogLevel);
|
||||
LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
|
||||
_discordLogger = LogManager.CreateLogger("Discord");
|
||||
_restLogger = LogManager.CreateLogger("Rest");
|
||||
_queueLogger = LogManager.CreateLogger("Queue");
|
||||
|
||||
_connectionLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
@@ -267,6 +267,8 @@ namespace Discord
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
ConnectionState IDiscordClient.ConnectionState => ConnectionState.Disconnected;
|
||||
ILogManager IDiscordClient.LogManager => LogManager;
|
||||
|
||||
Task IDiscordClient.ConnectAsync() { throw new NotSupportedException(); }
|
||||
Task IDiscordClient.DisconnectAsync() { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ namespace Discord
|
||||
remove { _connectedEvent.Remove(value); }
|
||||
}
|
||||
private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>();
|
||||
public event Func<Task> Disconnected
|
||||
public event Func<Exception, Task> Disconnected
|
||||
{
|
||||
add { _disconnectedEvent.Add(value); }
|
||||
remove { _disconnectedEvent.Remove(value); }
|
||||
}
|
||||
private readonly AsyncEvent<Func<Task>> _disconnectedEvent = new AsyncEvent<Func<Task>>();
|
||||
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
|
||||
public event Func<Task> Ready
|
||||
{
|
||||
add { _readyEvent.Add(value); }
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Discord
|
||||
AudioMode = config.AudioMode;
|
||||
WebSocketProvider = config.WebSocketProvider;
|
||||
|
||||
_gatewayLogger = _log.CreateLogger("Gateway");
|
||||
_gatewayLogger = LogManager.CreateLogger("Gateway");
|
||||
#if BENCHMARK
|
||||
_benchmarkLogger = _log.CreateLogger("Benchmark");
|
||||
#endif
|
||||
@@ -94,7 +94,7 @@ namespace Discord
|
||||
if (ex != null)
|
||||
{
|
||||
await _gatewayLogger.WarningAsync($"Connection Closed: {ex.Message}").ConfigureAwait(false);
|
||||
await StartReconnectAsync().ConfigureAwait(false);
|
||||
await StartReconnectAsync(ex).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await _gatewayLogger.WarningAsync($"Connection Closed").ConfigureAwait(false);
|
||||
@@ -112,7 +112,7 @@ namespace Discord
|
||||
protected override async Task OnLogoutAsync()
|
||||
{
|
||||
if (ConnectionState != ConnectionState.Disconnected)
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
|
||||
_voiceRegions = ImmutableDictionary.Create<string, VoiceRegion>();
|
||||
}
|
||||
@@ -142,7 +142,7 @@ namespace Discord
|
||||
|
||||
var state = ConnectionState;
|
||||
if (state == ConnectionState.Connecting || state == ConnectionState.Connected)
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
|
||||
ConnectionState = ConnectionState.Connecting;
|
||||
await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false);
|
||||
@@ -165,7 +165,7 @@ namespace Discord
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -176,11 +176,11 @@ namespace Discord
|
||||
try
|
||||
{
|
||||
_isReconnecting = false;
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
await DisconnectInternalAsync(null).ConfigureAwait(false);
|
||||
}
|
||||
finally { _connectionLock.Release(); }
|
||||
}
|
||||
private async Task DisconnectInternalAsync()
|
||||
private async Task DisconnectInternalAsync(Exception ex)
|
||||
{
|
||||
ulong guildId;
|
||||
|
||||
@@ -211,10 +211,10 @@ namespace Discord
|
||||
ConnectionState = ConnectionState.Disconnected;
|
||||
await _gatewayLogger.InfoAsync("Disconnected").ConfigureAwait(false);
|
||||
|
||||
await _disconnectedEvent.InvokeAsync().ConfigureAwait(false);
|
||||
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task StartReconnectAsync()
|
||||
private async Task StartReconnectAsync(Exception ex)
|
||||
{
|
||||
//TODO: Is this thread-safe?
|
||||
if (_reconnectTask != null) return;
|
||||
@@ -222,6 +222,7 @@ namespace Discord
|
||||
await _connectionLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await DisconnectInternalAsync(ex).ConfigureAwait(false);
|
||||
if (_reconnectTask != null) return;
|
||||
_isReconnecting = true;
|
||||
_reconnectTask = ReconnectInternalAsync();
|
||||
@@ -469,7 +470,7 @@ namespace Discord
|
||||
await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false);
|
||||
await _gatewayLogger.WarningAsync("Server requested a reconnect").ConfigureAwait(false);
|
||||
|
||||
await StartReconnectAsync().ConfigureAwait(false);
|
||||
await StartReconnectAsync(new Exception("Server requested a reconnect")).ConfigureAwait(false);
|
||||
}
|
||||
break;
|
||||
case GatewayOpCode.Dispatch:
|
||||
@@ -1113,9 +1114,7 @@ namespace Discord
|
||||
|
||||
var user = guild.GetUser(data.UserId);
|
||||
if (user != null)
|
||||
{
|
||||
await _userVoiceStateUpdatedEvent.InvokeAsync(user, before, after).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _gatewayLogger.WarningAsync("VOICE_STATE_UPDATE referenced an unknown user.").ConfigureAwait(false);
|
||||
@@ -1131,7 +1130,21 @@ namespace Discord
|
||||
}
|
||||
break;
|
||||
case "VOICE_SERVER_UPDATE":
|
||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
|
||||
await _gatewayLogger.DebugAsync("Received Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
|
||||
|
||||
if (AudioMode != AudioMode.Disabled)
|
||||
{
|
||||
var data = (payload as JToken).ToObject<VoiceServerUpdateEvent>(_serializer);
|
||||
var guild = DataStore.GetGuild(data.GuildId);
|
||||
if (guild != null)
|
||||
await guild.ConnectAudio("wss://" + data.Endpoint, data.Token).ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
await _gatewayLogger.WarningAsync("VOICE_SERVER_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
//Ignored (User only)
|
||||
@@ -1183,7 +1196,7 @@ namespace Discord
|
||||
if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? false))
|
||||
{
|
||||
await _gatewayLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false);
|
||||
await StartReconnectAsync().ConfigureAwait(false);
|
||||
await StartReconnectAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ChannelModel = Discord.API.Channel;
|
||||
using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent;
|
||||
@@ -17,8 +18,9 @@ using VoiceStateModel = Discord.API.VoiceState;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
internal class CachedGuild : Guild, IUserGuild, ICachedEntity<ulong>
|
||||
internal class CachedGuild : Guild, ICachedEntity<ulong>, IGuild, IUserGuild
|
||||
{
|
||||
private readonly SemaphoreSlim _audioLock;
|
||||
private TaskCompletionSource<bool> _downloaderPromise;
|
||||
private ConcurrentHashSet<ulong> _channels;
|
||||
private ConcurrentDictionary<ulong, CachedGuildUser> _members;
|
||||
@@ -27,7 +29,7 @@ namespace Discord
|
||||
public bool Available { get; private set; }
|
||||
public int MemberCount { get; private set; }
|
||||
public int DownloadedMemberCount { get; private set; }
|
||||
public IAudioClient AudioClient { get; private set; }
|
||||
public AudioClient AudioClient { get; private set; }
|
||||
|
||||
public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
|
||||
public Task DownloaderPromise => _downloaderPromise.Task;
|
||||
@@ -48,6 +50,7 @@ namespace Discord
|
||||
|
||||
public CachedGuild(DiscordSocketClient discord, ExtendedModel model, DataStore dataStore) : base(discord, model)
|
||||
{
|
||||
_audioLock = new SemaphoreSlim(1, 1);
|
||||
_downloaderPromise = new TaskCompletionSource<bool>();
|
||||
Update(model, UpdateSource.Creation, dataStore);
|
||||
}
|
||||
@@ -236,6 +239,55 @@ namespace Discord
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task ConnectAudio(string url, string token)
|
||||
{
|
||||
AudioClient audioClient;
|
||||
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||
var voiceState = GetVoiceState(CurrentUser.Id).Value;
|
||||
try
|
||||
{
|
||||
audioClient = AudioClient;
|
||||
if (audioClient == null)
|
||||
{
|
||||
audioClient = new AudioClient(this);
|
||||
audioClient.Disconnected += async ex =>
|
||||
{
|
||||
await _audioLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (ex != null)
|
||||
{
|
||||
//Reconnect if we still have channel info.
|
||||
//TODO: Is this threadsafe? Could channel data be deleted before we access it?
|
||||
var voiceState2 = GetVoiceState(CurrentUser.Id);
|
||||
if (voiceState2.HasValue)
|
||||
{
|
||||
var voiceChannelId = voiceState2.Value.VoiceChannel?.Id;
|
||||
if (voiceChannelId != null)
|
||||
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, voiceChannelId, voiceState2.Value.IsSelfDeafened, voiceState2.Value.IsSelfMuted);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try { AudioClient.Dispose(); } catch { }
|
||||
AudioClient = null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_audioLock.Release();
|
||||
}
|
||||
};
|
||||
AudioClient = audioClient;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_audioLock.Release();
|
||||
}
|
||||
await audioClient.ConnectAsync(url, CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public CachedGuild Clone() => MemberwiseClone() as CachedGuild;
|
||||
|
||||
new internal ICachedGuildChannel ToChannel(ChannelModel model)
|
||||
@@ -253,5 +305,6 @@ namespace Discord
|
||||
|
||||
bool IUserGuild.IsOwner => OwnerId == Discord.CurrentUser.Id;
|
||||
GuildPermissions IUserGuild.Permissions => CurrentUser.GuildPermissions;
|
||||
IAudioClient IGuild.AudioClient => AudioClient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Discord.API;
|
||||
using Discord.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -13,6 +14,7 @@ namespace Discord
|
||||
ConnectionState ConnectionState { get; }
|
||||
|
||||
DiscordApiClient ApiClient { get; }
|
||||
ILogManager LogManager { get; }
|
||||
|
||||
Task LoginAsync(TokenType tokenType, string token, bool validateToken = true);
|
||||
Task LogoutAsync();
|
||||
|
||||
Reference in New Issue
Block a user