Files
Discord.Net/src/Discord.Net.Audio/AudioService.cs

180 lines
6.1 KiB
C#

using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
namespace Discord.Audio
{
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;
internal DiscordClient Client { get; private set; }
public AudioServiceConfig Config { get; }
public event EventHandler Connected = delegate { };
public event EventHandler<VoiceDisconnectedEventArgs> Disconnected = delegate { };
public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated = delegate { };
private void OnConnected()
=> Connected(this, EventArgs.Empty);
private void OnDisconnected(ulong serverId, bool wasUnexpected, Exception ex)
=> Disconnected(this, new VoiceDisconnectedEventArgs(serverId, wasUnexpected, ex));
private void OnUserIsSpeakingUpdated(User user, bool isSpeaking)
=> UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, isSpeaking));
public AudioService(AudioServiceConfig config)
{
Config = config;
_asyncLock = new AsyncLock();
}
void IService.Install(DiscordClient client)
{
Client = client;
Config.Lock();
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)
{
AudioClient client;
if (_voiceClients.TryGetValue(server.Id, out client))
return client;
else
return null;
}
else
{
if (server == _currentClient.Server)
return _currentClient;
else
return null;
}
}
//Called from AudioClient.Disconnect
internal async Task RemoveClient(Server server, AudioClient client)
{
using (await _asyncLock.LockAsync().ConfigureAwait(false))
{
if (_voiceClients.TryUpdate(server.Id, null, client))
_voiceClients.TryRemove(server.Id, out client);
}
}
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
{
if (_defaultClient.Server != server)
{
await _defaultClient.Disconnect();
_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;
}
}
}
public async Task Leave(Server server)
{
if (server == null) throw new ArgumentNullException(nameof(server));
if (Config.EnableMultiserver)
{
AudioClient client;
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)
await _defaultClient.Disconnect().ConfigureAwait(false);
}
}
}
}
}