Browse Source

Removed hack; code now assumes client is logged in as Bot Account, without 1 voice channel limit

pull/49/head
Khionu 9 years ago
parent
commit
58025d303f
2 changed files with 73 additions and 201 deletions
  1. +19
    -72
      src/Discord.Net.Audio/AudioClient.cs
  2. +54
    -129
      src/Discord.Net.Audio/AudioService.cs

+ 19
- 72
src/Discord.Net.Audio/AudioClient.cs View File

@@ -1,5 +1,4 @@
using Discord.API.Client.GatewaySocket; using Discord.API.Client.GatewaySocket;
using Discord.API.Client.Rest;
using Discord.Logging; using Discord.Logging;
using Discord.Net.Rest; using Discord.Net.Rest;
using Discord.Net.WebSockets; using Discord.Net.WebSockets;
@@ -13,7 +12,7 @@ using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
internal class AudioClient : IAudioClient
internal class AudioClient : IAudioClient
{ {
private class OutStream : Stream private class OutStream : Stream
{ {
@@ -50,7 +49,7 @@ namespace Discord.Audio
private ConnectionState _gatewayState; private ConnectionState _gatewayState;


internal Logger Logger { get; } internal Logger Logger { get; }
public int Id { get; } public int Id { get; }
public AudioService Service { get; } public AudioService Service { get; }
public AudioServiceConfig Config { get; } public AudioServiceConfig Config { get; }
@@ -59,7 +58,7 @@ namespace Discord.Audio
public VoiceSocket VoiceSocket { get; } public VoiceSocket VoiceSocket { get; }
public JsonSerializer Serializer { get; } public JsonSerializer Serializer { get; }
public Stream OutputStream { get; } public Stream OutputStream { get; }
public CancellationToken CancelToken { get; private set; } public CancellationToken CancelToken { get; private set; }
public string SessionId => GatewaySocket.SessionId; public string SessionId => GatewaySocket.SessionId;


@@ -68,7 +67,7 @@ namespace Discord.Audio
public Channel Channel => VoiceSocket.Channel; public Channel Channel => VoiceSocket.Channel;


public AudioClient(DiscordClient client, Server server, int id) public AudioClient(DiscordClient client, Server server, int id)
{
{
Id = id; Id = id;
Service = client.GetService<AudioService>(); Service = client.GetService<AudioService>();
Config = Service.Config; Config = Service.Config;
@@ -84,40 +83,8 @@ namespace Discord.Audio
CancelToken = new CancellationToken(true); CancelToken = new CancellationToken(true);


//Networking //Networking
if (Config.EnableMultiserver)
{
//TODO: We can remove this hack when official API launches
var baseConfig = client.Config;
var builder = new DiscordConfigBuilder
{
AppName = baseConfig.AppName,
AppUrl = baseConfig.AppUrl,
AppVersion = baseConfig.AppVersion,
CacheToken = baseConfig.CacheDir != null,
ConnectionTimeout = baseConfig.ConnectionTimeout,
EnablePreUpdateEvents = false,
FailedReconnectDelay = baseConfig.FailedReconnectDelay,
LargeThreshold = 1,
LogLevel = baseConfig.LogLevel,
MessageCacheSize = 0,
ReconnectDelay = baseConfig.ReconnectDelay,
UsePermissionsCache = false
};
_config = builder.Build();

ClientAPI = new JsonRestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
GatewaySocket = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
GatewaySocket.Connected += (s, e) =>
{
if (_gatewayState == ConnectionState.Connecting)
EndGatewayConnect();
};
}
else
{
_config = client.Config;
GatewaySocket = client.GatewaySocket;
}
_config = client.Config;
GatewaySocket = client.GatewaySocket;
GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e); GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}")); VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}"));
VoiceSocket.Server = server; VoiceSocket.Server = server;
@@ -126,14 +93,9 @@ namespace Discord.Audio


public async Task Connect() public async Task Connect()
{ {
if (Config.EnableMultiserver)
await BeginGatewayConnect().ConfigureAwait(false);
else
{
var cancelSource = new CancellationTokenSource();
CancelToken = cancelSource.Token;
await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);
}
var cancelSource = new CancellationTokenSource();
CancelToken = cancelSource.Token;
await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);
} }
private async Task BeginGatewayConnect() private async Task BeginGatewayConnect()
{ {
@@ -154,7 +116,7 @@ namespace Discord.Audio
var cancelSource = new CancellationTokenSource(); var cancelSource = new CancellationTokenSource();
CancelToken = cancelSource.Token; CancelToken = cancelSource.Token;
ClientAPI.CancelToken = CancelToken; ClientAPI.CancelToken = CancelToken;
await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false); await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);


await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false); await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);
@@ -178,7 +140,7 @@ namespace Discord.Audio
{ {
_gatewayState = ConnectionState.Connected; _gatewayState = ConnectionState.Connected;
} }
public async Task Disconnect() public async Task Disconnect()
{ {
await _taskManager.Stop(true).ConfigureAwait(false); await _taskManager.Stop(true).ConfigureAwait(false);
@@ -188,28 +150,13 @@ namespace Discord.Audio
var oldState = _gatewayState; var oldState = _gatewayState;
_gatewayState = ConnectionState.Disconnecting; _gatewayState = ConnectionState.Disconnecting;


if (Config.EnableMultiserver)
{
if (oldState == ConnectionState.Connected)
{
try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
catch (OperationCanceledException) { }
}

await GatewaySocket.Disconnect().ConfigureAwait(false);
ClientAPI.Token = null;
}

var server = VoiceSocket.Server; var server = VoiceSocket.Server;
VoiceSocket.Server = null; VoiceSocket.Server = null;
VoiceSocket.Channel = null; VoiceSocket.Channel = null;
if (Config.EnableMultiserver)
await Service.RemoveClient(server, this).ConfigureAwait(false);
await Service.RemoveClient(server, this).ConfigureAwait(false);
SendVoiceUpdate(server.Id, null); SendVoiceUpdate(server.Id, null);


await VoiceSocket.Disconnect().ConfigureAwait(false); await VoiceSocket.Disconnect().ConfigureAwait(false);
if (Config.EnableMultiserver)
await GatewaySocket.Disconnect().ConfigureAwait(false);


_gatewayState = (int)ConnectionState.Disconnected; _gatewayState = (int)ConnectionState.Disconnected;
} }
@@ -222,7 +169,7 @@ namespace Discord.Audio
if (channel == VoiceSocket.Channel) return; if (channel == VoiceSocket.Channel) return;
var server = channel.Server; var server = channel.Server;
if (server != VoiceSocket.Server) if (server != VoiceSocket.Server)
throw new ArgumentException("This is channel is not part of the current server.", nameof(channel));
throw new ArgumentException("This channel is not part of the current server.", nameof(channel));
if (VoiceSocket.Server == null) if (VoiceSocket.Server == null)
throw new InvalidOperationException("This client has been closed."); throw new InvalidOperationException("This client has been closed.");


@@ -282,26 +229,26 @@ namespace Discord.Audio
} }


public void Send(byte[] data, int offset, int count) public void Send(byte[] data, int offset, int count)
{
{
if (data == null) throw new ArgumentException(nameof(data)); if (data == null) throw new ArgumentException(nameof(data));
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset)); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
if (VoiceSocket.Server == null) return; //Has been closed if (VoiceSocket.Server == null) return; //Has been closed
if (count == 0) return; if (count == 0) return;


VoiceSocket.SendPCMFrames(data, offset, count);
}
VoiceSocket.SendPCMFrames(data, offset, count);
}


public void Clear() public void Clear()
{ {
if (VoiceSocket.Server == null) return; //Has been closed if (VoiceSocket.Server == null) return; //Has been closed
VoiceSocket.ClearPCMFrames(); VoiceSocket.ClearPCMFrames();
} }
public void Wait()
public void Wait()
{ {
if (VoiceSocket.Server == null) return; //Has been closed if (VoiceSocket.Server == null) return; //Has been closed
VoiceSocket.WaitForQueue(); VoiceSocket.WaitForQueue();
}
}


public void SendVoiceUpdate(ulong? serverId, ulong? channelId) public void SendVoiceUpdate(ulong? serverId, ulong? channelId)
{ {
@@ -309,5 +256,5 @@ namespace Discord.Audio
(Service.Config.Mode | AudioMode.Outgoing) == 0, (Service.Config.Mode | AudioMode.Outgoing) == 0,
(Service.Config.Mode | AudioMode.Incoming) == 0); (Service.Config.Mode | AudioMode.Incoming) == 0);
} }
}
}
} }

+ 54
- 129
src/Discord.Net.Audio/AudioService.cs View File

@@ -1,22 +1,19 @@
using Nito.AsyncEx; using Nito.AsyncEx;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
public class AudioService : IService
public class AudioService : IService
{ {
private readonly AsyncLock _asyncLock; private readonly AsyncLock _asyncLock;
private AudioClient _defaultClient; //Only used for single server
private VirtualClient _currentClient; //Only used for single server
private ConcurrentDictionary<ulong, AudioClient> _voiceClients; private ConcurrentDictionary<ulong, AudioClient> _voiceClients;
private ConcurrentDictionary<User, bool> _talkingUsers;
private int _nextClientId;
private ConcurrentDictionary<User, bool> _talkingUsers;
private int _nextClientId;


public DiscordClient Client { get; private set; } public DiscordClient Client { get; private set; }
public AudioServiceConfig Config { get; }
public AudioServiceConfig Config { get; }


public event EventHandler Connected = delegate { }; public event EventHandler Connected = delegate { };
public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { }; public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { };
@@ -24,9 +21,9 @@ namespace Discord.Audio


private void OnConnected() private void OnConnected()
=> Connected(this, EventArgs.Empty); => Connected(this, EventArgs.Empty);
private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex)
private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex)
=> Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex)); => Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex));
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
=> UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking)); => UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));


public AudioService() public AudioService()
@@ -38,72 +35,41 @@ namespace Discord.Audio
{ {
} }
public AudioService(AudioServiceConfig config) public AudioService(AudioServiceConfig config)
{
{
Config = config; Config = config;
_asyncLock = new AsyncLock(); _asyncLock = new AsyncLock();


} }
void IService.Install(DiscordClient client)
{
Client = client;

if (Config.EnableMultiserver)
_voiceClients = new ConcurrentDictionary<ulong, AudioClient>();
else
{
var logger = Client.Log.CreateLogger("Voice");
_defaultClient = new AudioClient(Client, null, 0);
}
_talkingUsers = new ConcurrentDictionary<User, bool>();

client.GatewaySocket.Disconnected += async (s, e) =>
{
if (Config.EnableMultiserver)
{
var tasks = _voiceClients
.Select(x =>
{
var val = x.Value;
if (val != null)
return x.Value.Disconnect();
else
return TaskHelper.CompletedTask;
})
.ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
_voiceClients.Clear();
}
foreach (var member in _talkingUsers)
{
bool ignored;
if (_talkingUsers.TryRemove(member.Key, out ignored))
OnUserIsSpeakingUpdated(member.Key, false);
}
};
}

public IAudioClient GetClient(Server server)
{
if (server == null) throw new ArgumentNullException(nameof(server));

if (Config.EnableMultiserver)
void IService.Install(DiscordClient client)
{
Client = client;

_voiceClients = new ConcurrentDictionary<ulong, AudioClient>();

_talkingUsers = new ConcurrentDictionary<User, bool>();

client.GatewaySocket.Disconnected += (s, e) =>
{ {
AudioClient client;
if (_voiceClients.TryGetValue(server.Id, out client))
return client;
else
return null;
}
foreach (var member in _talkingUsers)
{
bool ignored;
if (_talkingUsers.TryRemove(member.Key, out ignored))
OnUserIsSpeakingUpdated(member.Key, false);
}
};
}

public IAudioClient GetClient(Server server)
{
if (server == null) throw new ArgumentNullException(nameof(server));
AudioClient client;
if (_voiceClients.TryGetValue(server.Id, out client))
return client;
else else
{
if (server == _currentClient.Server)
return _currentClient;
else
return null;
}
}
//Called from AudioClient.Disconnect
return null;
}

//Called from AudioClient.Cleanup
internal async Task RemoveClient(Server server, AudioClient client) internal async Task RemoveClient(Server server, AudioClient client)
{ {
using (await _asyncLock.LockAsync().ConfigureAwait(false)) using (await _asyncLock.LockAsync().ConfigureAwait(false))
@@ -113,81 +79,40 @@ namespace Discord.Audio
} }
} }


public async Task<IAudioClient> Join(Channel channel)
{
if (channel == null) throw new ArgumentNullException(nameof(channel));
public async Task<IAudioClient> Join(Channel channel)
{
if (channel == null) throw new ArgumentNullException(nameof(channel));
var server = channel.Server; var server = channel.Server;
using (await _asyncLock.LockAsync().ConfigureAwait(false)) using (await _asyncLock.LockAsync().ConfigureAwait(false))
{ {
if (Config.EnableMultiserver)
{
AudioClient client;
if (!_voiceClients.TryGetValue(server.Id, out client))
{
client = new AudioClient(Client, server, unchecked(++_nextClientId));
_voiceClients[server.Id] = client;

await client.Connect().ConfigureAwait(false);

/*voiceClient.VoiceSocket.FrameReceived += (s, e) =>
{
OnFrameReceieved(e);
};
voiceClient.VoiceSocket.UserIsSpeaking += (s, e) =>
{
var user = server.GetUser(e.UserId);
OnUserIsSpeakingUpdated(user, e.IsSpeaking);
};*/
}

await client.Join(channel).ConfigureAwait(false);
return client;
}
else
AudioClient client;
if (!_voiceClients.TryGetValue(server.Id, out client))
{ {
if (_defaultClient.Server != server)
{
await _defaultClient.Disconnect().ConfigureAwait(false);
_defaultClient.VoiceSocket.Server = server;
await _defaultClient.Connect().ConfigureAwait(false);
}
var client = new VirtualClient(_defaultClient, server);
_currentClient = client;

await client.Join(channel).ConfigureAwait(false);
return client;
client = new AudioClient(Client, server, unchecked(++_nextClientId));
_voiceClients[server.Id] = client;

await client.Connect().ConfigureAwait(false);
} }


await client.Join(channel).ConfigureAwait(false);
return client;
} }
}
}


public Task Leave(Server server) => Leave(server, null);
public Task Leave(Server server) => Leave(server, null);
public Task Leave(Channel channel) => Leave(channel.Server, channel); public Task Leave(Channel channel) => Leave(channel.Server, channel);
private async Task Leave(Server server, Channel channel) private async Task Leave(Server server, Channel channel)
{ {
if (server == null) throw new ArgumentNullException(nameof(server)); if (server == null) throw new ArgumentNullException(nameof(server));


if (Config.EnableMultiserver)
AudioClient client;
//Potential race condition if changing channels during this call, but that's acceptable
if (channel == null || (_voiceClients.TryGetValue(server.Id, out client) && client.Channel == channel))
{ {
AudioClient client;
//Potential race condition if changing channels during this call, but that's acceptable
if (channel == null || (_voiceClients.TryGetValue(server.Id, out client) && client.Channel == channel))
{
if (_voiceClients.TryRemove(server.Id, out client))
await client.Disconnect().ConfigureAwait(false);
}
}
else
{
using (await _asyncLock.LockAsync().ConfigureAwait(false))
{
var client = GetClient(server) as VirtualClient;
if (client != null && client.Channel == channel)
await _defaultClient.Disconnect().ConfigureAwait(false);
}
if (_voiceClients.TryRemove(server.Id, out client))
await client.Disconnect().ConfigureAwait(false);
} }

} }
} }
} }

Loading…
Cancel
Save