Fixed several voice stability issues, redesigned single-server voice
This commit is contained in:
@@ -45,6 +45,9 @@
|
|||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="..\Discord.Net.Audio\AudioClient.cs">
|
||||||
|
<Link>AudioClient.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net.Audio\AudioExtensions.cs">
|
<Compile Include="..\Discord.Net.Audio\AudioExtensions.cs">
|
||||||
<Link>AudioExtensions.cs</Link>
|
<Link>AudioExtensions.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -54,8 +57,8 @@
|
|||||||
<Compile Include="..\Discord.Net.Audio\AudioServiceConfig.cs">
|
<Compile Include="..\Discord.Net.Audio\AudioServiceConfig.cs">
|
||||||
<Link>AudioServiceConfig.cs</Link>
|
<Link>AudioServiceConfig.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net.Audio\DiscordAudioClient.cs">
|
<Compile Include="..\Discord.Net.Audio\IAudioClient.cs">
|
||||||
<Link>DiscordAudioClient.cs</Link>
|
<Link>IAudioClient.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net.Audio\Net\WebSockets\VoiceWebSocket.cs">
|
<Compile Include="..\Discord.Net.Audio\Net\WebSockets\VoiceWebSocket.cs">
|
||||||
<Link>Net\WebSockets\VoiceWebSocket.cs</Link>
|
<Link>Net\WebSockets\VoiceWebSocket.cs</Link>
|
||||||
@@ -72,6 +75,9 @@
|
|||||||
<Compile Include="..\Discord.Net.Audio\Opus\OpusEncoder.cs">
|
<Compile Include="..\Discord.Net.Audio\Opus\OpusEncoder.cs">
|
||||||
<Link>Opus\OpusEncoder.cs</Link>
|
<Link>Opus\OpusEncoder.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Discord.Net.Audio\SimpleAudioClient.cs">
|
||||||
|
<Link>SimpleAudioClient.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\Discord.Net.Audio\Sodium.cs">
|
<Compile Include="..\Discord.Net.Audio\Sodium.cs">
|
||||||
<Link>Sodium.cs</Link>
|
<Link>Sodium.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord.Audio
|
namespace Discord.Audio
|
||||||
{
|
{
|
||||||
public partial class DiscordAudioClient
|
internal class AudioClient : IAudioClient
|
||||||
{
|
{
|
||||||
private readonly Semaphore _connectionLock;
|
private readonly Semaphore _connectionLock;
|
||||||
private readonly JsonSerializer _serializer;
|
private readonly JsonSerializer _serializer;
|
||||||
@@ -20,19 +20,19 @@ namespace Discord.Audio
|
|||||||
public GatewaySocket GatewaySocket { get; }
|
public GatewaySocket GatewaySocket { get; }
|
||||||
public VoiceWebSocket VoiceSocket { get; }
|
public VoiceWebSocket VoiceSocket { get; }
|
||||||
|
|
||||||
public ulong? ServerId => VoiceSocket.ServerId;
|
|
||||||
public ulong? ChannelId => VoiceSocket.ChannelId;
|
|
||||||
public ConnectionState State => VoiceSocket.State;
|
public ConnectionState State => VoiceSocket.State;
|
||||||
|
public Server Server => VoiceSocket.Server;
|
||||||
|
public Channel Channel => VoiceSocket.Channel;
|
||||||
|
|
||||||
public DiscordAudioClient(AudioService service, int id, Logger logger, GatewaySocket gatewaySocket)
|
public AudioClient(AudioService service, int clientId, Server server, GatewaySocket gatewaySocket, Logger logger)
|
||||||
{
|
{
|
||||||
Service = service;
|
Service = service;
|
||||||
Id = id;
|
Id = clientId;
|
||||||
Logger = logger;
|
|
||||||
GatewaySocket = gatewaySocket;
|
GatewaySocket = gatewaySocket;
|
||||||
|
Logger = logger;
|
||||||
_connectionLock = new Semaphore(1, 1);
|
|
||||||
|
_connectionLock = new Semaphore(1, 1);
|
||||||
|
|
||||||
_serializer = new JsonSerializer();
|
_serializer = new JsonSerializer();
|
||||||
_serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
|
_serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
|
||||||
_serializer.Error += (s, e) =>
|
_serializer.Error += (s, e) =>
|
||||||
@@ -41,7 +41,10 @@ namespace Discord.Audio
|
|||||||
Logger.Error("Serialization Failed", e.ErrorContext.Error);
|
Logger.Error("Serialization Failed", e.ErrorContext.Error);
|
||||||
};
|
};
|
||||||
|
|
||||||
VoiceSocket = new VoiceWebSocket(service.Client, this, _serializer, logger);
|
GatewaySocket.ReceivedDispatch += OnReceivedDispatch;
|
||||||
|
|
||||||
|
VoiceSocket = new VoiceWebSocket(service.Client, this, _serializer, logger);
|
||||||
|
VoiceSocket.Server = server;
|
||||||
|
|
||||||
/*_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
|
/*_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
|
||||||
_voiceSocket.Disconnected += async (s, e) =>
|
_voiceSocket.Disconnected += async (s, e) =>
|
||||||
@@ -76,58 +79,54 @@ namespace Discord.Audio
|
|||||||
{
|
{
|
||||||
_voiceSocket.ParentCancelToken = _cancelToken;
|
_voiceSocket.ParentCancelToken = _cancelToken;
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal async Task SetServer(ulong serverId)
|
public async Task Join(Channel channel)
|
||||||
{
|
|
||||||
if (serverId != VoiceSocket.ServerId)
|
|
||||||
{
|
|
||||||
await Disconnect().ConfigureAwait(false);
|
|
||||||
VoiceSocket.ServerId = serverId;
|
|
||||||
VoiceSocket.ChannelId = null;
|
|
||||||
SendVoiceUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Task JoinChannel(Channel channel)
|
|
||||||
{
|
{
|
||||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||||
var serverId = channel.Server?.Id;
|
if (channel.Type != ChannelType.Voice)
|
||||||
var channelId = channel.Id;
|
throw new ArgumentException("Channel must be a voice channel.", nameof(channel));
|
||||||
if (serverId != ServerId)
|
if (channel.Server != VoiceSocket.Server)
|
||||||
throw new InvalidOperationException("Cannot join a channel on a different server than this voice client.");
|
throw new ArgumentException("This is channel is not part of the current server.", nameof(channel));
|
||||||
if (channelId == VoiceSocket.ChannelId)
|
if (channel == VoiceSocket.Channel) return;
|
||||||
return TaskHelper.CompletedTask;
|
if (VoiceSocket.Server == null)
|
||||||
//CheckReady(checkVoice: true);
|
throw new InvalidOperationException("This client has been closed.");
|
||||||
|
|
||||||
return Task.Run(async () =>
|
|
||||||
{
|
|
||||||
_connectionLock.WaitOne();
|
_connectionLock.WaitOne();
|
||||||
GatewaySocket.ReceivedDispatch += OnReceivedDispatch;
|
try
|
||||||
try
|
{
|
||||||
|
_cancelTokenSource = new CancellationTokenSource();
|
||||||
|
var cancelToken = _cancelTokenSource.Token;
|
||||||
|
VoiceSocket.ParentCancelToken = cancelToken;
|
||||||
|
VoiceSocket.Channel = channel;
|
||||||
|
|
||||||
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (State != ConnectionState.Disconnected)
|
|
||||||
await Disconnect().ConfigureAwait(false);
|
|
||||||
|
|
||||||
_cancelTokenSource = new CancellationTokenSource();
|
|
||||||
var cancelToken = _cancelTokenSource.Token;
|
|
||||||
VoiceSocket.ParentCancelToken = cancelToken;
|
|
||||||
|
|
||||||
VoiceSocket.ChannelId = channelId;
|
|
||||||
SendVoiceUpdate();
|
SendVoiceUpdate();
|
||||||
|
|
||||||
VoiceSocket.WaitForConnection(cancelToken);
|
VoiceSocket.WaitForConnection(cancelToken);
|
||||||
}
|
});
|
||||||
finally
|
}
|
||||||
{
|
finally
|
||||||
GatewaySocket.ReceivedDispatch -= OnReceivedDispatch;
|
{
|
||||||
_connectionLock.Release();
|
_connectionLock.Release();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
public async Task Disconnect()
|
||||||
|
{
|
||||||
|
_connectionLock.WaitOne();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Service.RemoveClient(VoiceSocket.Server, this);
|
||||||
|
VoiceSocket.Channel = null;
|
||||||
|
SendVoiceUpdate();
|
||||||
|
await VoiceSocket.Disconnect();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_connectionLock.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public Task Disconnect()
|
|
||||||
=> VoiceSocket.Disconnect();
|
|
||||||
|
|
||||||
private async void OnReceivedDispatch(object sender, WebSocketEventEventArgs e)
|
private async void OnReceivedDispatch(object sender, WebSocketEventEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -135,12 +134,31 @@ namespace Discord.Audio
|
|||||||
{
|
{
|
||||||
switch (e.Type)
|
switch (e.Type)
|
||||||
{
|
{
|
||||||
|
case "VOICE_STATE_UPDATE":
|
||||||
|
{
|
||||||
|
var data = e.Payload.ToObject<VoiceStateUpdateEvent>(_serializer);
|
||||||
|
if (data.GuildId == VoiceSocket.Server?.Id && data.UserId == Service.Client.CurrentUser?.Id)
|
||||||
|
{
|
||||||
|
if (data.ChannelId == null)
|
||||||
|
await Disconnect();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var channel = Service.Client.GetChannel(data.ChannelId.Value);
|
||||||
|
if (channel != null)
|
||||||
|
VoiceSocket.Channel = channel;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Warning("VOICE_STATE_UPDATE referenced an unknown channel, disconnecting.");
|
||||||
|
await Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "VOICE_SERVER_UPDATE":
|
case "VOICE_SERVER_UPDATE":
|
||||||
{
|
{
|
||||||
var data = e.Payload.ToObject<VoiceServerUpdateEvent>(_serializer);
|
var data = e.Payload.ToObject<VoiceServerUpdateEvent>(_serializer);
|
||||||
var serverId = data.GuildId;
|
if (data.GuildId == VoiceSocket.Server?.Id)
|
||||||
|
|
||||||
if (serverId == ServerId)
|
|
||||||
{
|
{
|
||||||
var client = Service.Client;
|
var client = Service.Client;
|
||||||
VoiceSocket.Token = data.Token;
|
VoiceSocket.Token = data.Token;
|
||||||
@@ -164,34 +182,32 @@ namespace Discord.Audio
|
|||||||
{
|
{
|
||||||
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));
|
||||||
//CheckReady(checkVoice: true);
|
if (VoiceSocket.Server == null) return; //Has been closed
|
||||||
|
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
VoiceSocket.SendPCMFrames(data, count);
|
VoiceSocket.SendPCMFrames(data, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Clears the PCM buffer. </summary>
|
/// <summary> Clears the PCM buffer. </summary>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
//CheckReady(checkVoice: true);
|
if (VoiceSocket.Server == null) return; //Has been closed
|
||||||
|
VoiceSocket.ClearPCMFrames();
|
||||||
VoiceSocket.ClearPCMFrames();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Returns a task that completes once the voice output buffer is empty. </summary>
|
/// <summary> Returns a task that completes once the voice output buffer is empty. </summary>
|
||||||
public void Wait()
|
public void Wait()
|
||||||
{
|
{
|
||||||
//CheckReady(checkVoice: true);
|
if (VoiceSocket.Server == null) return; //Has been closed
|
||||||
|
VoiceSocket.WaitForQueue();
|
||||||
VoiceSocket.WaitForQueue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendVoiceUpdate()
|
private void SendVoiceUpdate()
|
||||||
{
|
{
|
||||||
var serverId = VoiceSocket.ServerId;
|
var serverId = VoiceSocket.Server?.Id;
|
||||||
if (serverId != null)
|
if (serverId != null)
|
||||||
{
|
{
|
||||||
GatewaySocket.SendUpdateVoice(serverId, VoiceSocket.ChannelId,
|
GatewaySocket.SendUpdateVoice(serverId, VoiceSocket.Channel?.Id,
|
||||||
(Service.Config.Mode | AudioMode.Outgoing) == 0,
|
(Service.Config.Mode | AudioMode.Outgoing) == 0,
|
||||||
(Service.Config.Mode | AudioMode.Incoming) == 0);
|
(Service.Config.Mode | AudioMode.Incoming) == 0);
|
||||||
}
|
}
|
||||||
@@ -46,8 +46,8 @@ namespace Discord.Audio
|
|||||||
|
|
||||||
public class AudioService : IService
|
public class AudioService : IService
|
||||||
{
|
{
|
||||||
private DiscordAudioClient _defaultClient;
|
private AudioClient _defaultClient;
|
||||||
private ConcurrentDictionary<ulong, DiscordAudioClient> _voiceClients;
|
private ConcurrentDictionary<ulong, IAudioClient> _voiceClients;
|
||||||
private ConcurrentDictionary<User, bool> _talkingUsers;
|
private ConcurrentDictionary<User, bool> _talkingUsers;
|
||||||
//private int _nextClientId;
|
//private int _nextClientId;
|
||||||
|
|
||||||
@@ -91,11 +91,11 @@ namespace Discord.Audio
|
|||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
if (Config.EnableMultiserver)
|
if (Config.EnableMultiserver)
|
||||||
_voiceClients = new ConcurrentDictionary<ulong, DiscordAudioClient>();
|
_voiceClients = new ConcurrentDictionary<ulong, IAudioClient>();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var logger = Client.Log.CreateLogger("Voice");
|
var logger = Client.Log.CreateLogger("Voice");
|
||||||
_defaultClient = new DiscordAudioClient(this, 0, logger, _client.GatewaySocket);
|
_defaultClient = new SimpleAudioClient(this, 0, logger);
|
||||||
}
|
}
|
||||||
_talkingUsers = new ConcurrentDictionary<User, bool>();
|
_talkingUsers = new ConcurrentDictionary<User, bool>();
|
||||||
|
|
||||||
@@ -118,34 +118,29 @@ namespace Discord.Audio
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscordAudioClient GetClient(Server server)
|
public IAudioClient GetClient(Server server)
|
||||||
{
|
{
|
||||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||||
|
|
||||||
if (!Config.EnableMultiserver)
|
if (!Config.EnableMultiserver)
|
||||||
{
|
{
|
||||||
if (server.Id == _defaultClient.ServerId)
|
if (server == _defaultClient.Server)
|
||||||
return _defaultClient;
|
return (_defaultClient as SimpleAudioClient).CurrentClient;
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscordAudioClient client;
|
|
||||||
if (_voiceClients.TryGetValue(server.Id, out client))
|
|
||||||
return client;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
private async Task<DiscordAudioClient> CreateClient(Server server)
|
|
||||||
{
|
|
||||||
if (!Config.EnableMultiserver)
|
|
||||||
{
|
|
||||||
await _defaultClient.SetServer(server.Id);
|
|
||||||
return _defaultClient;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
throw new InvalidOperationException("Multiserver voice is not currently supported");
|
{
|
||||||
|
IAudioClient client;
|
||||||
|
if (_voiceClients.TryGetValue(server.Id, out client))
|
||||||
|
return client;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Task<IAudioClient> CreateClient(Server server)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
/*var client = _voiceClients.GetOrAdd(server.Id, _ =>
|
/*var client = _voiceClients.GetOrAdd(server.Id, _ =>
|
||||||
{
|
{
|
||||||
int id = unchecked(++_nextClientId);
|
int id = unchecked(++_nextClientId);
|
||||||
@@ -169,30 +164,44 @@ namespace Discord.Audio
|
|||||||
return Task.FromResult(client);*/
|
return Task.FromResult(client);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DiscordAudioClient> Join(Channel channel)
|
//TODO: This isn't threadsafe
|
||||||
|
internal void RemoveClient(Server server, IAudioClient client)
|
||||||
|
{
|
||||||
|
if (Config.EnableMultiserver && server != null)
|
||||||
|
_voiceClients.TryRemove(server.Id, out client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IAudioClient> Join(Channel channel)
|
||||||
{
|
{
|
||||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||||
//CheckReady(true);
|
|
||||||
|
|
||||||
var client = await CreateClient(channel.Server).ConfigureAwait(false);
|
IAudioClient client;
|
||||||
await client.JoinChannel(channel).ConfigureAwait(false);
|
if (!Config.EnableMultiserver)
|
||||||
|
client = await (_defaultClient as SimpleAudioClient).Connect(channel).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client = await CreateClient(channel.Server).ConfigureAwait(false);
|
||||||
|
await client.Join(channel).ConfigureAwait(false);
|
||||||
|
}
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Leave(Server server)
|
public async Task Leave(Server server)
|
||||||
{
|
{
|
||||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||||
//CheckReady(true);
|
|
||||||
|
|
||||||
if (Config.EnableMultiserver)
|
if (Config.EnableMultiserver)
|
||||||
{
|
{
|
||||||
//client.CheckReady();
|
IAudioClient client;
|
||||||
DiscordAudioClient client;
|
if (_voiceClients.TryRemove(server.Id, out client))
|
||||||
if (_voiceClients.TryRemove(server.Id, out client))
|
await client.Disconnect().ConfigureAwait(false);
|
||||||
await client.Disconnect().ConfigureAwait(false);
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
await _defaultClient.Disconnect().ConfigureAwait(false);
|
IAudioClient client = GetClient(server);
|
||||||
|
if (client != null)
|
||||||
|
await (_defaultClient as SimpleAudioClient).Leave(client as SimpleAudioClient.VirtualClient).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/Discord.Net.Audio/IAudioClient.cs
Normal file
23
src/Discord.Net.Audio/IAudioClient.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.Audio
|
||||||
|
{
|
||||||
|
public interface IAudioClient
|
||||||
|
{
|
||||||
|
ConnectionState State { get; }
|
||||||
|
Channel Channel { get; }
|
||||||
|
Server Server { get; }
|
||||||
|
|
||||||
|
Task Join(Channel channel);
|
||||||
|
Task Disconnect();
|
||||||
|
|
||||||
|
/// <summary> Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. </summary>
|
||||||
|
/// <param name="data">PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. </param>
|
||||||
|
/// <param name="count">Number of bytes in this frame. </param>
|
||||||
|
void Send(byte[] data, int count);
|
||||||
|
/// <summary> Clears the PCM buffer. </summary>
|
||||||
|
void Clear();
|
||||||
|
/// <summary> Blocks until the voice output buffer is empty. </summary>
|
||||||
|
void Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
private readonly int _targetAudioBufferLength;
|
private readonly int _targetAudioBufferLength;
|
||||||
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
|
private readonly ConcurrentDictionary<uint, OpusDecoder> _decoders;
|
||||||
private readonly DiscordAudioClient _audioClient;
|
private readonly AudioClient _audioClient;
|
||||||
private readonly AudioServiceConfig _config;
|
private readonly AudioServiceConfig _config;
|
||||||
private Thread _sendThread, _receiveThread;
|
private Thread _sendThread, _receiveThread;
|
||||||
private VoiceBuffer _sendBuffer;
|
private VoiceBuffer _sendBuffer;
|
||||||
@@ -44,13 +44,13 @@ namespace Discord.Net.WebSockets
|
|||||||
private int _ping;
|
private int _ping;
|
||||||
|
|
||||||
public string Token { get; internal set; }
|
public string Token { get; internal set; }
|
||||||
public ulong? ServerId { get; internal set; }
|
public Server Server { get; internal set; }
|
||||||
public ulong? ChannelId { get; internal set; }
|
public Channel Channel { get; internal set; }
|
||||||
|
|
||||||
public int Ping => _ping;
|
public int Ping => _ping;
|
||||||
internal VoiceBuffer OutputBuffer => _sendBuffer;
|
internal VoiceBuffer OutputBuffer => _sendBuffer;
|
||||||
|
|
||||||
public VoiceWebSocket(DiscordClient client, DiscordAudioClient audioClient, JsonSerializer serializer, Logger logger)
|
internal VoiceWebSocket(DiscordClient client, AudioClient audioClient, JsonSerializer serializer, Logger logger)
|
||||||
: base(client, serializer, logger)
|
: base(client, serializer, logger)
|
||||||
{
|
{
|
||||||
_audioClient = audioClient;
|
_audioClient = audioClient;
|
||||||
@@ -65,7 +65,7 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
public Task Connect()
|
public Task Connect()
|
||||||
=> BeginConnect();
|
=> BeginConnect();
|
||||||
public async Task Reconnect()
|
private async Task Reconnect()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -234,7 +234,7 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
ulong userId;
|
ulong userId;
|
||||||
if (_ssrcMapping.TryGetValue(ssrc, out userId))
|
if (_ssrcMapping.TryGetValue(ssrc, out userId))
|
||||||
RaiseOnPacket(userId, ChannelId.Value, result, resultOffset, resultLength);
|
RaiseOnPacket(userId, Channel.Id, result, resultOffset, resultLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,7 +477,7 @@ namespace Discord.Net.WebSockets
|
|||||||
public override void SendHeartbeat()
|
public override void SendHeartbeat()
|
||||||
=> QueueMessage(new HeartbeatCommand());
|
=> QueueMessage(new HeartbeatCommand());
|
||||||
public void SendIdentify()
|
public void SendIdentify()
|
||||||
=> QueueMessage(new IdentifyCommand { GuildId = ServerId.Value, UserId = _client.CurrentUser.Id,
|
=> QueueMessage(new IdentifyCommand { GuildId = Server.Id, UserId = _client.CurrentUser.Id,
|
||||||
SessionId = _client.SessionId, Token = Token });
|
SessionId = _client.SessionId, Token = Token });
|
||||||
public void SendSelectProtocol(string externalAddress, int externalPort)
|
public void SendSelectProtocol(string externalAddress, int externalPort)
|
||||||
=> QueueMessage(new SelectProtocolCommand { Protocol = "udp", ExternalAddress = externalAddress,
|
=> QueueMessage(new SelectProtocolCommand { Protocol = "udp", ExternalAddress = externalAddress,
|
||||||
|
|||||||
79
src/Discord.Net.Audio/SimpleAudioClient.cs
Normal file
79
src/Discord.Net.Audio/SimpleAudioClient.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using Discord.Logging;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.Audio
|
||||||
|
{
|
||||||
|
internal class SimpleAudioClient : AudioClient
|
||||||
|
{
|
||||||
|
internal class VirtualClient : IAudioClient
|
||||||
|
{
|
||||||
|
private readonly SimpleAudioClient _client;
|
||||||
|
|
||||||
|
ConnectionState IAudioClient.State => _client.VoiceSocket.State;
|
||||||
|
Server IAudioClient.Server => _client.VoiceSocket.Server;
|
||||||
|
Channel IAudioClient.Channel => _client.VoiceSocket.Channel;
|
||||||
|
|
||||||
|
public VirtualClient(SimpleAudioClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task IAudioClient.Disconnect() => _client.Leave(this);
|
||||||
|
Task IAudioClient.Join(Channel channel) => _client.Join(channel);
|
||||||
|
|
||||||
|
void IAudioClient.Send(byte[] data, int count) => _client.Send(data, count);
|
||||||
|
void IAudioClient.Clear() => _client.Clear();
|
||||||
|
void IAudioClient.Wait() => _client.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Semaphore _connectionLock;
|
||||||
|
|
||||||
|
internal VirtualClient CurrentClient { get; private set; }
|
||||||
|
|
||||||
|
public SimpleAudioClient(AudioService service, int id, Logger logger)
|
||||||
|
: base(service, id, null, service.Client.GatewaySocket, logger)
|
||||||
|
{
|
||||||
|
_connectionLock = new Semaphore(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only disconnects if is current a member of this server
|
||||||
|
public async Task Leave(VirtualClient client)
|
||||||
|
{
|
||||||
|
_connectionLock.WaitOne();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CurrentClient == client)
|
||||||
|
{
|
||||||
|
CurrentClient = null;
|
||||||
|
await Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_connectionLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<IAudioClient> Connect(Channel channel)
|
||||||
|
{
|
||||||
|
_connectionLock.WaitOne();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool changeServer = channel.Server != VoiceSocket.Server;
|
||||||
|
if (changeServer || CurrentClient == null)
|
||||||
|
{
|
||||||
|
await Disconnect().ConfigureAwait(false);
|
||||||
|
CurrentClient = new VirtualClient(this);
|
||||||
|
VoiceSocket.Server = channel.Server;
|
||||||
|
}
|
||||||
|
await Join(channel);
|
||||||
|
return CurrentClient;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_connectionLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using System.Security;
|
|||||||
|
|
||||||
namespace Discord.Audio.Sodium
|
namespace Discord.Audio.Sodium
|
||||||
{
|
{
|
||||||
public unsafe static class SecretBox
|
internal unsafe static class SecretBox
|
||||||
{
|
{
|
||||||
#if NET45
|
#if NET45
|
||||||
[SuppressUnmanagedCodeSecurity]
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
|||||||
Reference in New Issue
Block a user