Browse Source

Finished AudioClient connection handshake

tags/1.0-rc
RogueException 9 years ago
parent
commit
689c9ff31a
12 changed files with 247 additions and 55 deletions
  1. +3
    -3
      src/Discord.Net/API/DiscordAPIClient.cs
  2. +92
    -22
      src/Discord.Net/API/DiscordVoiceAPIClient.cs
  3. +16
    -0
      src/Discord.Net/API/Voice/ReadyEvent.cs
  4. +12
    -0
      src/Discord.Net/API/Voice/SelectProtocolParams.cs
  5. +12
    -0
      src/Discord.Net/API/Voice/SessionDescriptionEvent.cs
  6. +12
    -0
      src/Discord.Net/API/Voice/SpeakingParams.cs
  7. +14
    -0
      src/Discord.Net/API/Voice/UdpProtocolInfo.cs
  8. +76
    -23
      src/Discord.Net/Audio/AudioClient.cs
  9. +1
    -2
      src/Discord.Net/DiscordClient.cs
  10. +5
    -3
      src/Discord.Net/DiscordSocketClient.cs
  11. +2
    -2
      src/Discord.Net/Entities/WebSocket/CachedGuild.cs
  12. +2
    -0
      src/Discord.Net/project.json

+ 3
- 3
src/Discord.Net/API/DiscordAPIClient.cs View File

@@ -27,8 +27,8 @@ namespace Discord.API

public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
public event Func<int, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<int, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<int, Task>>();
public event Func<GatewayOpCode, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<GatewayOpCode, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<GatewayOpCode, Task>>();

public event Func<GatewayOpCode, int?, string, object, Task> ReceivedGatewayEvent { add { _receivedGatewayEvent.Add(value); } remove { _receivedGatewayEvent.Remove(value); } }
private readonly AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>> _receivedGatewayEvent = new AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>>();
@@ -352,7 +352,7 @@ namespace Discord.API
if (payload != null)
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
await _requestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync((int)opCode).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false);
}

//Auth


+ 92
- 22
src/Discord.Net/API/DiscordVoiceAPIClient.cs View File

@@ -11,28 +11,37 @@ using System.IO.Compression;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace Discord.Audio
{
public class DiscordVoiceAPIClient
{
public const int MaxBitrate = 128;
private const string Mode = "xsalsa20_poly1305";
public const string Mode = "xsalsa20_poly1305";

public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
public event Func<int, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<int, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<int, Task>>();
public event Func<VoiceOpCode, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<VoiceOpCode, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<VoiceOpCode, Task>>();
public event Func<Task> SentDiscovery { add { _sentDiscoveryEvent.Add(value); } remove { _sentDiscoveryEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _sentDiscoveryEvent = new AsyncEvent<Func<Task>>();

public event Func<VoiceOpCode, object, Task> ReceivedEvent { add { _receivedEvent.Add(value); } remove { _receivedEvent.Remove(value); } }
private readonly AsyncEvent<Func<VoiceOpCode, object, Task>> _receivedEvent = new AsyncEvent<Func<VoiceOpCode, object, Task>>();
public event Func<byte[], Task> ReceivedPacket { add { _receivedPacketEvent.Add(value); } remove { _receivedPacketEvent.Remove(value); } }
private readonly AsyncEvent<Func<byte[], Task>> _receivedPacketEvent = new AsyncEvent<Func<byte[], 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 JsonSerializer _serializer;
private readonly IWebSocketClient _gatewayClient;
private readonly IWebSocketClient _webSocketClient;
private readonly SemaphoreSlim _connectionLock;
private CancellationTokenSource _connectCancelToken;
private UdpClient _udp;
private IPEndPoint _udpEndpoint;
private Task _udpRecieveTask;
private bool _isDisposed;

public ulong GuildId { get; }
@@ -42,10 +51,11 @@ namespace Discord.Audio
{
GuildId = guildId;
_connectionLock = new SemaphoreSlim(1, 1);
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));

_gatewayClient = webSocketProvider();
_webSocketClient = webSocketProvider();
//_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+)
_gatewayClient.BinaryMessage += async (data, index, count) =>
_webSocketClient.BinaryMessage += async (data, index, count) =>
{
using (var compressed = new MemoryStream(data, index + 2, count - 2))
using (var decompressed = new MemoryStream())
@@ -60,12 +70,12 @@ namespace Discord.Audio
}
}
};
_gatewayClient.TextMessage += async text =>
_webSocketClient.TextMessage += async text =>
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(text);
await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
};
_gatewayClient.Closed += async ex =>
_webSocketClient.Closed += async ex =>
{
await DisconnectAsync().ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
@@ -80,21 +90,29 @@ namespace Discord.Audio
if (disposing)
{
_connectCancelToken?.Dispose();
(_gatewayClient as IDisposable)?.Dispose();
(_webSocketClient as IDisposable)?.Dispose();
}
_isDisposed = true;
}
}
public void Dispose() => Dispose(true);

public Task SendAsync(VoiceOpCode opCode, object payload, RequestOptions options = null)
public async Task SendAsync(VoiceOpCode opCode, object payload, RequestOptions options = null)
{
byte[] bytes = null;
payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload };
if (payload != null)
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
//TODO: Send
return Task.CompletedTask;
await _webSocketClient.SendAsync(bytes, 0, bytes.Length, true).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync(opCode);
}
public async Task SendAsync(byte[] data, int bytes)
{
if (_udpEndpoint != null)
{
await _udp.SendAsync(data, bytes, _udpEndpoint).ConfigureAwait(false);
await _sentDiscoveryEvent.InvokeAsync().ConfigureAwait(false);
}
}

//WebSocket
@@ -102,36 +120,56 @@ namespace Discord.Audio
{
await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false);
}
public async Task SendIdentityAsync(ulong guildId, ulong userId, string sessionId, string token)
public async Task SendIdentityAsync(ulong userId, string sessionId, string token)
{
await SendAsync(VoiceOpCode.Identify, new IdentifyParams
{
GuildId = guildId,
GuildId = GuildId,
UserId = userId,
SessionId = sessionId,
Token = token
});
}
public async Task SendSelectProtocol(string externalIp, int externalPort)
{
await SendAsync(VoiceOpCode.SelectProtocol, new SelectProtocolParams
{
Protocol = "udp",
Data = new UdpProtocolInfo
{
Address = externalIp,
Port = externalPort,
Mode = Mode
}
});
}
public async Task SendSetSpeaking(bool value)
{
await SendAsync(VoiceOpCode.Speaking, new SpeakingParams
{
IsSpeaking = value,
Delay = 0
});
}

public async Task ConnectAsync(string url, ulong userId, string sessionId, string token)
public async Task ConnectAsync(string url)
{
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
await ConnectInternalAsync(url, userId, sessionId, token).ConfigureAwait(false);
await ConnectInternalAsync(url).ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
}
private async Task ConnectInternalAsync(string url, ulong userId, string sessionId, string token)
private async Task ConnectInternalAsync(string url)
{
ConnectionState = ConnectionState.Connecting;
try
{
_connectCancelToken = new CancellationTokenSource();
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
await _gatewayClient.ConnectAsync(url).ConfigureAwait(false);

await SendIdentityAsync(GuildId, userId, sessionId, token).ConfigureAwait(false);
_webSocketClient.SetCancelToken(_connectCancelToken.Token);
await _webSocketClient.ConnectAsync(url).ConfigureAwait(false);
_udpRecieveTask = ReceiveAsync(_connectCancelToken.Token);

ConnectionState = ConnectionState.Connected;
}
@@ -159,11 +197,43 @@ namespace Discord.Audio
try { _connectCancelToken?.Cancel(false); }
catch { }

await _gatewayClient.DisconnectAsync().ConfigureAwait(false);
//Wait for tasks to complete
await _udpRecieveTask.ConfigureAwait(false);

await _webSocketClient.DisconnectAsync().ConfigureAwait(false);

ConnectionState = ConnectionState.Disconnected;
}

//Udp
public async Task SendDiscoveryAsync(uint ssrc)
{
var packet = new byte[70];
packet[0] = (byte)(ssrc >> 24);
packet[1] = (byte)(ssrc >> 16);
packet[2] = (byte)(ssrc >> 8);
packet[3] = (byte)(ssrc >> 0);
await SendAsync(packet, 70).ConfigureAwait(false);
}

public void SetUdpEndpoint(IPEndPoint endpoint)
{
_udpEndpoint = endpoint;
}
private async Task ReceiveAsync(CancellationToken cancelToken)
{
var closeTask = Task.Delay(-1, cancelToken);
while (!cancelToken.IsCancellationRequested)
{
var receiveTask = _udp.ReceiveAsync();
var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false);
if (task == closeTask)
break;
await _receivedPacketEvent.InvokeAsync(receiveTask.Result.Buffer).ConfigureAwait(false);
}
}

//Helpers
private static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
private string SerializeJson(object value)


+ 16
- 0
src/Discord.Net/API/Voice/ReadyEvent.cs View File

@@ -0,0 +1,16 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
public class ReadyEvent
{
[JsonProperty("ssrc")]
public uint SSRC { get; set; }
[JsonProperty("port")]
public ushort Port { get; set; }
[JsonProperty("modes")]
public string[] Modes { get; set; }
[JsonProperty("heartbeat_interval")]
public int HeartbeatInterval { get; set; }
}
}

+ 12
- 0
src/Discord.Net/API/Voice/SelectProtocolParams.cs View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
public class SelectProtocolParams
{
[JsonProperty("protocol")]
public string Protocol { get; set; }
[JsonProperty("data")]
public UdpProtocolInfo Data { get; set; }
}
}

+ 12
- 0
src/Discord.Net/API/Voice/SessionDescriptionEvent.cs View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
public class SessionDescriptionEvent
{
[JsonProperty("secret_key")]
public byte[] SecretKey { get; set; }
[JsonProperty("mode")]
public string Mode { get; set; }
}
}

+ 12
- 0
src/Discord.Net/API/Voice/SpeakingParams.cs View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
public class SpeakingParams
{
[JsonProperty("speaking")]
public bool IsSpeaking { get; set; }
[JsonProperty("delay")]
public int Delay { get; set; }
}
}

+ 14
- 0
src/Discord.Net/API/Voice/UdpProtocolInfo.cs View File

@@ -0,0 +1,14 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
public class UdpProtocolInfo
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("port")]
public int Port { get; set; }
[JsonProperty("mode")]
public string Mode { get; set; }
}
}

+ 76
- 23
src/Discord.Net/Audio/AudioClient.cs View File

@@ -2,7 +2,11 @@
using Discord.Logging;
using Discord.Net.Converters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

@@ -29,7 +33,7 @@ namespace Discord.Audio
}
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();

private readonly ILogger _webSocketLogger, _udpLogger;
private readonly ILogger _audioLogger;
#if BENCHMARK
private readonly ILogger _benchmarkLogger;
#endif
@@ -42,6 +46,8 @@ namespace Discord.Audio
private long _heartbeatTime;
private string _url;
private bool _isDisposed;
private uint _ssrc;
private byte[] _secretKey;

public CachedGuild Guild { get; }
public DiscordVoiceAPIClient ApiClient { get; private set; }
@@ -51,12 +57,11 @@ namespace Discord.Audio
private DiscordSocketClient Discord => Guild.Discord;

/// <summary> Creates a new REST/WebSocket discord client. </summary>
internal AudioClient(CachedGuild guild)
internal AudioClient(CachedGuild guild, int id)
{
Guild = guild;

_webSocketLogger = Discord.LogManager.CreateLogger("Audio");
_udpLogger = Discord.LogManager.CreateLogger("AudioUDP");
_audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}");
#if BENCHMARK
_benchmarkLogger = logManager.CreateLogger("Benchmark");
#endif
@@ -66,20 +71,22 @@ namespace Discord.Audio
_serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
_serializer.Error += (s, e) =>
{
_webSocketLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
_audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
e.ErrorContext.Handled = true;
};
ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider);

ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false);
ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false);
ApiClient.ReceivedEvent += ProcessMessageAsync;
ApiClient.ReceivedPacket += ProcessPacketAsync;
ApiClient.Disconnected += async ex =>
{
if (ex != null)
await _webSocketLogger.WarningAsync($"Connection Closed: {ex.Message}").ConfigureAwait(false);
await _audioLogger.WarningAsync($"Connection Closed: {ex.Message}").ConfigureAwait(false);
else
await _webSocketLogger.WarningAsync($"Connection Closed").ConfigureAwait(false);
await _audioLogger.WarningAsync($"Connection Closed").ConfigureAwait(false);
};
}

@@ -100,19 +107,20 @@ namespace Discord.Audio
await DisconnectInternalAsync(null).ConfigureAwait(false);

ConnectionState = ConnectionState.Connecting;
await _webSocketLogger.InfoAsync("Connecting").ConfigureAwait(false);
await _audioLogger.InfoAsync("Connecting").ConfigureAwait(false);
try
{
_url = url;
_connectTask = new TaskCompletionSource<bool>();
_cancelToken = new CancellationTokenSource();
await ApiClient.ConnectAsync(url, userId, sessionId, token).ConfigureAwait(false);
await _connectedEvent.InvokeAsync().ConfigureAwait(false);

await ApiClient.ConnectAsync("wss://" + url).ConfigureAwait(false);
await ApiClient.SendIdentityAsync(userId, sessionId, token).ConfigureAwait(false);
await _connectTask.Task.ConfigureAwait(false);

await _connectedEvent.InvokeAsync().ConfigureAwait(false);
ConnectionState = ConnectionState.Connected;
await _webSocketLogger.InfoAsync("Connected").ConfigureAwait(false);
await _audioLogger.InfoAsync("Connected").ConfigureAwait(false);
}
catch (Exception)
{
@@ -143,7 +151,7 @@ namespace Discord.Audio
{
if (ConnectionState == ConnectionState.Disconnected) return;
ConnectionState = ConnectionState.Disconnecting;
await _webSocketLogger.InfoAsync("Disconnecting").ConfigureAwait(false);
await _audioLogger.InfoAsync("Disconnecting").ConfigureAwait(false);

//Signal tasks to complete
try { _cancelToken.Cancel(); } catch { }
@@ -158,7 +166,7 @@ namespace Discord.Audio
_heartbeatTask = null;

ConnectionState = ConnectionState.Disconnected;
await _webSocketLogger.InfoAsync("Disconnected").ConfigureAwait(false);
await _audioLogger.InfoAsync("Disconnected").ConfigureAwait(false);

await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
}
@@ -174,25 +182,49 @@ namespace Discord.Audio
{
switch (opCode)
{
/*case VoiceOpCode.Ready:
case VoiceOpCode.Ready:
{
await _webSocketLogger.DebugAsync("Received Ready").ConfigureAwait(false);
await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false);
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);

_ssrc = data.SSRC;

if (!data.Modes.Contains(DiscordVoiceAPIClient.Mode))
throw new InvalidOperationException($"Discord does not support {DiscordVoiceAPIClient.Mode}");

_heartbeatTime = 0;
_heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token);

var entry = await Dns.GetHostEntryAsync(_url).ConfigureAwait(false);

ApiClient.SetUdpEndpoint(new IPEndPoint(entry.AddressList[0], data.Port));
await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false);
}
break;*/
break;
case VoiceOpCode.SessionDescription:
{
await _audioLogger.DebugAsync("Received SessionDescription").ConfigureAwait(false);
var data = (payload as JToken).ToObject<SessionDescriptionEvent>(_serializer);

if (data.Mode != DiscordVoiceAPIClient.Mode)
throw new InvalidOperationException($"Discord selected an unexpected mode: {data.Mode}");

_secretKey = data.SecretKey;
await ApiClient.SendSetSpeaking(true).ConfigureAwait(false);

_connectTask.TrySetResult(true);
}
break;
case VoiceOpCode.HeartbeatAck:
{
await _webSocketLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false);
await _audioLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false);

var heartbeatTime = _heartbeatTime;
if (heartbeatTime != 0)
{
int latency = (int)(Environment.TickCount - _heartbeatTime);
_heartbeatTime = 0;
await _webSocketLogger.VerboseAsync($"Latency = {latency} ms").ConfigureAwait(false);
await _audioLogger.VerboseAsync($"Latency = {latency} ms").ConfigureAwait(false);

int before = Latency;
Latency = latency;
@@ -202,13 +234,13 @@ namespace Discord.Audio
}
break;
default:
await _webSocketLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false);
await _audioLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false);
return;
}
}
catch (Exception ex)
{
await _webSocketLogger.ErrorAsync($"Error handling {opCode}", ex).ConfigureAwait(false);
await _audioLogger.ErrorAsync($"Error handling {opCode}", ex).ConfigureAwait(false);
return;
}
#if BENCHMARK
@@ -222,6 +254,27 @@ namespace Discord.Audio
#endif
}

private async Task ProcessPacketAsync(byte[] packet)
{
if (!_connectTask.Task.IsCompleted)
{
if (packet.Length == 70)
{
string ip;
int port;
try
{
ip = Encoding.UTF8.GetString(packet, 4, 70 - 6).TrimEnd('\0');
port = packet[68] | packet[69] << 8;
}
catch { return; }
await _audioLogger.DebugAsync("Received Discovery").ConfigureAwait(false);
await ApiClient.SendSelectProtocol(ip, port);
}
}
}

private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cancelToken)
{
//Clean this up when Discord's session patch is live
@@ -235,7 +288,7 @@ namespace Discord.Audio
{
if (ConnectionState == ConnectionState.Connected)
{
await _webSocketLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false);
await _audioLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false);
await DisconnectInternalAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false);
return;
}


+ 1
- 2
src/Discord.Net/DiscordClient.cs View File

@@ -35,8 +35,7 @@ namespace Discord
public LoginState LoginState { get; private set; }

/// <summary> Creates a new REST-only discord client. </summary>
public DiscordClient()
: this(new DiscordConfig()) { }
public DiscordClient() : this(new DiscordConfig()) { }
/// <summary> Creates a new REST-only discord client. </summary>
public DiscordClient(DiscordConfig config)
{


+ 5
- 3
src/Discord.Net/DiscordSocketClient.cs View File

@@ -35,6 +35,7 @@ namespace Discord
private bool _isReconnecting;
private int _unavailableGuilds;
private long _lastGuildAvailableTime;
private int _nextAudioId;

/// <summary> Gets the shard if of this client. </summary>
public int ShardId { get; }
@@ -74,6 +75,7 @@ namespace Discord
LargeThreshold = config.LargeThreshold;
AudioMode = config.AudioMode;
WebSocketProvider = config.WebSocketProvider;
_nextAudioId = 1;

_gatewayLogger = LogManager.CreateLogger("Gateway");
#if BENCHMARK
@@ -87,7 +89,7 @@ namespace Discord
e.ErrorContext.Handled = true;
};
ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {(GatewayOpCode)opCode}").ConfigureAwait(false);
ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.ReceivedGatewayEvent += ProcessMessageAsync;
ApiClient.Disconnected += async ex =>
{
@@ -1173,8 +1175,8 @@ namespace Discord
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
string endpoint = "wss://" + data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':'));
await guild.ConnectAudio(endpoint, data.Token).ConfigureAwait(false);
string endpoint = data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':'));
var _ = guild.ConnectAudio(_nextAudioId++, endpoint, data.Token).ConfigureAwait(false);
}
else
{


+ 2
- 2
src/Discord.Net/Entities/WebSocket/CachedGuild.cs View File

@@ -261,7 +261,7 @@ namespace Discord
return null;
}

public async Task ConnectAudio(string url, string token)
public async Task ConnectAudio(int id, string url, string token)
{
AudioClient audioClient;
await _audioLock.WaitAsync().ConfigureAwait(false);
@@ -271,7 +271,7 @@ namespace Discord
audioClient = AudioClient;
if (audioClient == null)
{
audioClient = new AudioClient(this);
audioClient = new AudioClient(this, id);
audioClient.Disconnected += async ex =>
{
await _audioLock.WaitAsync().ConfigureAwait(false);


+ 2
- 0
src/Discord.Net/project.json View File

@@ -26,6 +26,8 @@
"System.IO.Compression": "4.1.0",
"System.IO.FileSystem": "4.0.1",
"System.Net.Http": "4.1.0",
"System.Net.NameResolution": "4.0.0",
"System.Net.Sockets": "4.1.0",
"System.Net.WebSockets.Client": "4.0.0",
"System.Reflection.Extensions": "4.0.1",
"System.Runtime.InteropServices": "4.1.0",


Loading…
Cancel
Save