Browse Source

Merge pull request #49 from khionu/master

Removed hack
pull/52/head
RogueException 9 years ago
parent
commit
638fe71f6e
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.Rest;
using Discord.Logging;
using Discord.Net.Rest;
using Discord.Net.WebSockets;
@@ -13,7 +12,7 @@ using System.Threading.Tasks;

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

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

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

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

//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);
VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}"));
VoiceSocket.Server = server;
@@ -126,14 +93,9 @@ namespace Discord.Audio

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()
{
@@ -154,7 +116,7 @@ namespace Discord.Audio
var cancelSource = new CancellationTokenSource();
CancelToken = cancelSource.Token;
ClientAPI.CancelToken = CancelToken;
await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);

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

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

_gatewayState = (int)ConnectionState.Disconnected;
}
@@ -222,7 +169,7 @@ namespace Discord.Audio
if (channel == VoiceSocket.Channel) return;
var server = channel.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)
throw new InvalidOperationException("This client has been closed.");

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

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

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

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

public void SendVoiceUpdate(ulong? serverId, ulong? channelId)
{
@@ -309,5 +256,5 @@ namespace Discord.Audio
(Service.Config.Mode | AudioMode.Outgoing) == 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 System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;

namespace Discord.Audio
{
public class AudioService : IService
public class AudioService : IService
{
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<User, bool> _talkingUsers;
private int _nextClientId;
private ConcurrentDictionary<User, bool> _talkingUsers;
private int _nextClientId;

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

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

private void OnConnected()
=> 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));
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
=> UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));

public AudioService()
@@ -38,72 +35,41 @@ namespace Discord.Audio
{
}
public AudioService(AudioServiceConfig config)
{
{
Config = config;
_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
{
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)
{
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;
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);
private async Task Leave(Server server, Channel channel)
{
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