diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index dd677e651..394102fdc 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -136,15 +136,6 @@ DiscordAPIClient.cs - - DiscordBaseClient.cs - - - DiscordBaseClient.Events.cs - - - DiscordBaseClient.Voice.cs - DiscordClient.API.cs @@ -160,6 +151,15 @@ DiscordClientConfig.cs + + DiscordSimpleClient.cs + + + DiscordSimpleClient.Events.cs + + + DiscordSimpleClient.Voice.cs + Enums\ChannelTypes.cs diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 318743f2d..2a61a5690 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -6,19 +6,22 @@ using Discord.WebSockets.Data; using Newtonsoft.Json; using System; using System.Collections.Concurrent; +using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using VoiceWebSocket = Discord.WebSockets.Voice.VoiceWebSocket; namespace Discord { /// Provides a connection to the DiscordApp service. - public partial class DiscordClient : DiscordBaseClient + public partial class DiscordClient : DiscordSimpleClient { protected readonly DiscordAPIClient _api; private readonly Random _rand; private readonly JsonSerializer _serializer; private readonly ConcurrentQueue _pendingMessages; + private readonly ConcurrentDictionary _voiceClients; /// Returns the current logged-in user. public User CurrentUser => _currentUser; @@ -52,6 +55,8 @@ namespace Discord _api = new DiscordAPIClient(_config.LogLevel, _config.APITimeout); if (_config.UseMessageQueue) _pendingMessages = new ConcurrentQueue(); + if (_config.EnableVoiceMultiserver) + _voiceClients = new ConcurrentDictionary(); object cacheLock = new object(); _channels = new Channels(this, cacheLock); @@ -61,38 +66,20 @@ namespace Discord _servers = new Servers(this, cacheLock); _users = new Users(this, cacheLock); - if (Config.VoiceMode != DiscordVoiceMode.Disabled) + this.Connected += (s,e) => _api.CancelToken = CancelToken; + + VoiceDisconnected += (s, e) => { - this.VoiceDisconnected += (s, e) => - { - foreach (var member in _members) - { - if (member.IsSpeaking) - { - member.IsSpeaking = false; - RaiseUserIsSpeaking(member, false); - } - } - }; - _voiceSocket.IsSpeaking += (s, e) => + foreach (var member in _members) { - if (_voiceSocket.State == WebSocketState.Connected) + if (member.ServerId == e.ServerId && member.IsSpeaking) { - var member = _members[e.UserId, _voiceSocket.CurrentServerId]; - bool value = e.IsSpeaking; - if (member.IsSpeaking != value) - { - member.IsSpeaking = value; - RaiseUserIsSpeaking(member, value); - if (Config.TrackActivity) - member.UpdateActivity(); - } + member.IsSpeaking = false; + RaiseUserIsSpeaking(member, false); } - }; - } + } + }; - this.Connected += (s,e) => _api.CancelToken = CancelToken; - if (_config.LogLevel >= LogMessageSeverity.Verbose) { bool isDebug = _config.LogLevel >= LogMessageSeverity.Debug; @@ -207,6 +194,27 @@ namespace Discord #endif } + internal override VoiceWebSocket CreateVoiceSocket() + { + var socket = base.CreateVoiceSocket(); + socket.IsSpeaking += (s, e) => + { + if (_voiceSocket.State == WebSocketState.Connected) + { + var member = _members[e.UserId, socket.CurrentServerId]; + bool value = e.IsSpeaking; + if (member.IsSpeaking != value) + { + member.IsSpeaking = value; + RaiseUserIsSpeaking(member, value); + if (_config.TrackActivity) + member.UpdateActivity(); + } + } + }; + return socket; + } + /// Connects to the Discord server with the provided email and password. /// Returns a token for future connections. public new async Task Connect(string email, string password) @@ -249,6 +257,18 @@ namespace Discord { await base.Cleanup().ConfigureAwait(false); + if (_config.VoiceMode != DiscordVoiceMode.Disabled) + { + if (_config.EnableVoiceMultiserver) + { + var tasks = _voiceClients + .Select(x => x.Value.Disconnect()) + .ToArray(); + _voiceClients.Clear(); + await Task.WhenAll(tasks).ConfigureAwait(false); + } + } + if (_config.UseMessageQueue) { Message ignored; @@ -624,20 +644,6 @@ namespace Discord } } break; - case "VOICE_SERVER_UPDATE": - { - var data = e.Payload.ToObject(_serializer); - if (data.GuildId == _voiceSocket.CurrentServerId) - { - var server = _servers[data.GuildId]; - if (_config.VoiceMode != DiscordVoiceMode.Disabled) - { - _voiceSocket.Host = "wss://" + data.Endpoint.Split(':')[0]; - await _voiceSocket.Login(CurrentUserId, _dataSocket.SessionId, data.Token, CancelToken).ConfigureAwait(false); - } - } - } - break; //Settings case "USER_UPDATE": @@ -657,11 +663,76 @@ namespace Discord } break; + //Internal (handled in DiscordSimpleClient) + case "VOICE_SERVER_UPDATE": + break; + //Others default: RaiseOnLog(LogMessageSeverity.Warning, LogMessageSource.DataWebSocket, $"Unknown message type: {e.Type}"); break; } } + + public IDiscordVoiceClient GetVoiceClient(string serverId) + { + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + + if (!_config.EnableVoiceMultiserver) + { + if (serverId == _voiceServerId) + return this; + else + return null; + } + + DiscordSimpleClient client; + if (_voiceClients.TryGetValue(serverId, out client)) + return client; + else + return null; + } + private async Task CreateVoiceClient(string serverId) + { + if (!_config.EnableVoiceMultiserver) + { + _voiceServerId = serverId; + return this; + } + + var client = _voiceClients.GetOrAdd(serverId, _ => + { + var config = _config.Clone(); + config.LogLevel = (LogMessageSeverity)Math.Min((int)_config.LogLevel, (int)LogMessageSeverity.Warning); + config.EnableVoiceMultiserver = false; + return new DiscordSimpleClient(config, serverId); + }); + await client.Connect(_gateway, _token).ConfigureAwait(false); + return client; + } + + public Task JoinVoiceServer(Channel channel) + => JoinVoiceServer(channel?.ServerId, channel?.Id); + public Task JoinVoiceServer(Server server, string channelId) + => JoinVoiceServer(server?.Id, channelId); + public async Task JoinVoiceServer(string serverId, string channelId) + { + CheckReady(); //checkVoice is done inside the voice client + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + if (channelId == null) throw new ArgumentNullException(nameof(channelId)); + + var client = await CreateVoiceClient(serverId).ConfigureAwait(false); + await client.JoinChannel(channelId).ConfigureAwait(false); + } + + async Task LeaveVoiceServer(string serverId) + { + CheckReady(); //checkVoice is done inside the voice client + if (serverId == null) throw new ArgumentNullException(nameof(serverId)); + + DiscordSimpleClient client; + if (_voiceClients.TryRemove(serverId, out client)) + await client.Disconnect(); + } } } diff --git a/src/Discord.Net/DiscordClientConfig.cs b/src/Discord.Net/DiscordClientConfig.cs index 49006a990..422e1e022 100644 --- a/src/Discord.Net/DiscordClientConfig.cs +++ b/src/Discord.Net/DiscordClientConfig.cs @@ -11,7 +11,7 @@ namespace Discord Both = Outgoing | Incoming } - public class DiscordClientConfig + public sealed class DiscordClientConfig { /// Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } } @@ -72,5 +72,12 @@ namespace Discord throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); storage = value; } - } + + public DiscordClientConfig Clone() + { + var config = this.MemberwiseClone() as DiscordClientConfig; + config._isLocked = false; + return config; + } + } } diff --git a/src/Discord.Net/DiscordBaseClient.Events.cs b/src/Discord.Net/DiscordSimpleClient.Events.cs similarity index 83% rename from src/Discord.Net/DiscordBaseClient.Events.cs rename to src/Discord.Net/DiscordSimpleClient.Events.cs index ca1c01f65..739ed3a0f 100644 --- a/src/Discord.Net/DiscordBaseClient.Events.cs +++ b/src/Discord.Net/DiscordSimpleClient.Events.cs @@ -32,6 +32,16 @@ namespace Discord Error = error; } } + public class VoiceDisconnectedEventArgs : DisconnectedEventArgs + { + public readonly string ServerId; + + internal VoiceDisconnectedEventArgs(string serverId, DisconnectedEventArgs e) + : base(e.WasUnexpected, e.Error) + { + ServerId = serverId; + } + } public sealed class LogMessageEventArgs : EventArgs { public LogMessageSeverity Severity { get; } @@ -63,7 +73,7 @@ namespace Discord } } - public abstract partial class DiscordBaseClient + public partial class DiscordSimpleClient { public event EventHandler Connected; private void RaiseConnected() @@ -90,11 +100,11 @@ namespace Discord if (VoiceConnected != null) RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); } - public event EventHandler VoiceDisconnected; - private void RaiseVoiceDisconnected(DisconnectedEventArgs e) + public event EventHandler VoiceDisconnected; + private void RaiseVoiceDisconnected(string serverId, DisconnectedEventArgs e) { if (VoiceDisconnected != null) - RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, e)); + RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e))); } public event EventHandler OnVoicePacket; diff --git a/src/Discord.Net/DiscordBaseClient.Voice.cs b/src/Discord.Net/DiscordSimpleClient.Voice.cs similarity index 62% rename from src/Discord.Net/DiscordBaseClient.Voice.cs rename to src/Discord.Net/DiscordSimpleClient.Voice.cs index 1c5d5c7bb..b132a7a05 100644 --- a/src/Discord.Net/DiscordBaseClient.Voice.cs +++ b/src/Discord.Net/DiscordSimpleClient.Voice.cs @@ -6,47 +6,53 @@ using System.Threading.Tasks; namespace Discord { - public partial class DiscordBaseClient + public interface IDiscordVoiceClient { - public Task JoinVoiceServer(Channel channel) - => JoinVoiceServer(channel?.ServerId, channel?.Id); - public Task JoinVoiceServer(Server server, string channelId) - => JoinVoiceServer(server?.Id, channelId); - public async Task JoinVoiceServer(string serverId, string channelId) + Task JoinChannel(string channelId); + Task Disconnect(); + + void SendVoicePCM(byte[] data, int count); + void ClearVoicePCM(); + + Task WaitVoice(); + } + + public partial class DiscordSimpleClient : IDiscordVoiceClient + { + async Task IDiscordVoiceClient.JoinChannel(string channelId) { CheckReady(checkVoice: true); - if (serverId == null) throw new ArgumentNullException(nameof(serverId)); if (channelId == null) throw new ArgumentNullException(nameof(channelId)); - - await LeaveVoiceServer().ConfigureAwait(false); - _voiceSocket.SetChannel(serverId, channelId); - _dataSocket.SendJoinVoice(serverId, channelId); + + await ((IDiscordVoiceClient)this).Disconnect().ConfigureAwait(false); + _voiceSocket.SetChannel(_voiceServerId, channelId); + _dataSocket.SendJoinVoice(_voiceServerId, channelId); CancellationTokenSource tokenSource = new CancellationTokenSource(); try { - await Task.Run(() => _voiceSocket.WaitForConnection(tokenSource.Token)) + await Task.Run(() => _voiceSocket.WaitForConnection(tokenSource.Token)) .Timeout(_config.ConnectionTimeout, tokenSource) .ConfigureAwait(false); } catch (TimeoutException) { tokenSource.Cancel(); - await LeaveVoiceServer().ConfigureAwait(false); + await ((IDiscordVoiceClient)this).Disconnect().ConfigureAwait(false); throw; } } - public async Task LeaveVoiceServer() + + async Task IDiscordVoiceClient.Disconnect() { CheckReady(checkVoice: true); if (_voiceSocket.State != WebSocketState.Disconnected) { - var serverId = _voiceSocket.CurrentServerId; - if (serverId != null) + if (_voiceSocket.CurrentServerId != null) { await _voiceSocket.Disconnect().ConfigureAwait(false); - _dataSocket.SendLeaveVoice(serverId); + _dataSocket.SendLeaveVoice(_voiceSocket.CurrentServerId); } } } @@ -54,7 +60,7 @@ namespace Discord /// Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. /// PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. /// Number of bytes in this frame. - public void SendVoicePCM(byte[] data, int count) + void IDiscordVoiceClient.SendVoicePCM(byte[] data, int count) { CheckReady(checkVoice: true); if (data == null) throw new ArgumentException(nameof(data)); @@ -64,7 +70,7 @@ namespace Discord _voiceSocket.SendPCMFrames(data, count); } /// Clears the PCM buffer. - public void ClearVoicePCM() + void IDiscordVoiceClient.ClearVoicePCM() { CheckReady(checkVoice: true); @@ -72,7 +78,7 @@ namespace Discord } /// Returns a task that completes once the voice output buffer is empty. - public async Task WaitVoice() + async Task IDiscordVoiceClient.WaitVoice() { CheckReady(checkVoice: true); diff --git a/src/Discord.Net/DiscordBaseClient.cs b/src/Discord.Net/DiscordSimpleClient.cs similarity index 72% rename from src/Discord.Net/DiscordBaseClient.cs rename to src/Discord.Net/DiscordSimpleClient.cs index 936634eb6..386832e68 100644 --- a/src/Discord.Net/DiscordBaseClient.cs +++ b/src/Discord.Net/DiscordSimpleClient.cs @@ -1,10 +1,6 @@ -using Discord.API; -using Discord.Collections; -using Discord.Helpers; +using Discord.Helpers; using Discord.WebSockets.Data; using System; -using System.Collections.Concurrent; -using System.Net; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -21,18 +17,24 @@ namespace Discord } /// Provides a barebones connection to the Discord service - public partial class DiscordBaseClient + public partial class DiscordSimpleClient { internal readonly DataWebSocket _dataSocket; internal readonly VoiceWebSocket _voiceSocket; protected readonly ManualResetEvent _disconnectedEvent; protected readonly ManualResetEventSlim _connectedEvent; + protected readonly bool _enableVoice; + protected string _gateway, _token; + protected string _voiceServerId; private Task _runTask; - private string _gateway, _token; protected ExceptionDispatchInfo _disconnectReason; private bool _wasDisconnectUnexpected; + /// Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. + public DiscordClientConfig Config => _config; + protected readonly DiscordClientConfig _config; + /// Returns the id of the current logged-in user. public string CurrentUserId => _currentUserId; private string _currentUserId; @@ -43,64 +45,78 @@ namespace Discord public DiscordClientState State => (DiscordClientState)_state; private int _state; - /// Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. - public DiscordClientConfig Config => _config; - protected readonly DiscordClientConfig _config; - public CancellationToken CancelToken => _cancelToken; private CancellationTokenSource _cancelTokenSource; private CancellationToken _cancelToken; /// Initializes a new instance of the DiscordClient class. - public DiscordBaseClient(DiscordClientConfig config = null) + public DiscordSimpleClient(DiscordClientConfig config = null) { _config = config ?? new DiscordClientConfig(); _config.Lock(); + _enableVoice = config.VoiceMode != DiscordVoiceMode.Disabled && !config.EnableVoiceMultiserver; + _state = (int)DiscordClientState.Disconnected; _cancelToken = new CancellationToken(true); _disconnectedEvent = new ManualResetEvent(true); _connectedEvent = new ManualResetEventSlim(false); - _dataSocket = new DataWebSocket(this); - _dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); }; - _dataSocket.Disconnected += async (s, e) => + _dataSocket = CreateDataSocket(); + if (_enableVoice) + _voiceSocket = CreateVoiceSocket(); + } + internal DiscordSimpleClient(DiscordClientConfig config = null, string serverId = null) + : this(config) + { + _voiceServerId = serverId; + } + + internal virtual DataWebSocket CreateDataSocket() + { + var socket = new DataWebSocket(this); + socket.Connected += (s, e) => + { + if (_state == (int)DiscordClientState.Connecting) + CompleteConnect(); } + ; + socket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) - await _dataSocket.Reconnect(_token); + await socket.Reconnect(_token); }; - if (Config.VoiceMode != DiscordVoiceMode.Disabled) + socket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.DataWebSocket, e.Message); + if (_config.LogLevel >= LogMessageSeverity.Info) { - _voiceSocket = new VoiceWebSocket(this); - _voiceSocket.Connected += (s, e) => RaiseVoiceConnected(); - _voiceSocket.Disconnected += async (s, e) => - { - RaiseVoiceDisconnected(e); - if (e.WasUnexpected) - await _voiceSocket.Reconnect(); - }; + socket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected"); + socket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected"); } - _dataSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.DataWebSocket, e.Message); - if (_config.VoiceMode != DiscordVoiceMode.Disabled) - _voiceSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message); + socket.ReceivedEvent += (s, e) => OnReceivedEvent(e); + return socket; + } + internal virtual VoiceWebSocket CreateVoiceSocket() + { + var socket = new VoiceWebSocket(this); + socket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message); + socket.Connected += (s, e) => RaiseVoiceConnected(); + socket.Disconnected += async (s, e) => + { + RaiseVoiceDisconnected(socket.CurrentServerId, e); + if (e.WasUnexpected) + await socket.Reconnect(); + }; if (_config.LogLevel >= LogMessageSeverity.Info) { - _dataSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected"); - _dataSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected"); - if (_config.VoiceMode != DiscordVoiceMode.Disabled) - { - _voiceSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Connected"); - _voiceSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Disconnected"); - } + socket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Connected"); + socket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Disconnected"); } - - _dataSocket.ReceivedEvent += (s, e) => OnReceivedEvent(e); + return socket; } //Connection - protected async Task Connect(string gateway, string token) + public async Task Connect(string gateway, string token) { try { @@ -132,7 +148,6 @@ namespace Discord } //_state = (int)DiscordClientState.Connected; - _token = token; return token; } catch @@ -222,7 +237,7 @@ namespace Discord protected virtual async Task Cleanup() { await _dataSocket.Disconnect().ConfigureAwait(false); - if (_config.VoiceMode != DiscordVoiceMode.Disabled) + if (_enableVoice) await _voiceSocket.Disconnect().ConfigureAwait(false); _currentUserId = null; @@ -249,7 +264,7 @@ namespace Discord throw new InvalidOperationException("The client is connecting."); } - if (checkVoice && _config.VoiceMode == DiscordVoiceMode.Disabled) + if (checkVoice && !_enableVoice) throw new InvalidOperationException("Voice is not enabled for this client."); } protected void RaiseEvent(string name, Action action) @@ -264,8 +279,24 @@ namespace Discord internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e) { - if (e.Type == "READY") - _currentUserId = e.Payload["user"].Value("id"); + switch (e.Type) + { + case "READY": + _currentUserId = e.Payload["user"].Value("id"); + break; + case "VOICE_SERVER_UPDATE": + { + string guildId = e.Payload.Value("guild_id"); + + if (_enableVoice && guildId == _voiceSocket.CurrentServerId) + { + string token = e.Payload.Value("token"); + _voiceSocket.Host = "wss://" + e.Payload.Value("endpoint").Split(':')[0]; + return _voiceSocket.Login(_currentUserId, _dataSocket.SessionId, token, CancelToken); + } + } + break; + } return TaskHelper.CompletedTask; } } diff --git a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs index 54578d9aa..faa2c8d22 100644 --- a/src/Discord.Net/WebSockets/Data/DataWebSocket.cs +++ b/src/Discord.Net/WebSockets/Data/DataWebSocket.cs @@ -12,7 +12,7 @@ namespace Discord.WebSockets.Data public string SessionId => _sessionId; private string _sessionId; - public DataWebSocket(DiscordBaseClient client) + public DataWebSocket(DiscordSimpleClient client) : base(client) { } diff --git a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs index 03ec26ad5..beef201c8 100644 --- a/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs +++ b/src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; @@ -45,7 +46,7 @@ namespace Discord.WebSockets.Voice public string CurrentServerId => _serverId; public string CurrentChannelId => _channelId; - public VoiceWebSocket(DiscordBaseClient client) + public VoiceWebSocket(DiscordSimpleClient client) : base(client) { _rand = new Random(); @@ -121,25 +122,32 @@ namespace Discord.WebSockets.Voice msg.Payload.UserId = _userId; QueueMessage(msg); + List tasks = new List(); + if ((_client.Config.VoiceMode & DiscordVoiceMode.Outgoing) != 0) + { #if USE_THREAD - _sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken))); - _sendThread.Start(); - _receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken))); - _receiveThread.Start(); -#if !DNXCORE50 - return new Task[] { WatcherAsync() }.Concat(base.Run()).ToArray(); + _sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken))); + _sendThread.Start(); #else - return base.Run(); + tasks.Add(SendVoiceAsync()); #endif -#else //!USE_THREAD - return new Task[] { Task.WhenAll( - ReceiveVoiceAsync(), - SendVoiceAsync(), -#if !DNXCORE50 - WatcherAsync() + } + if ((_client.Config.VoiceMode & DiscordVoiceMode.Incoming) != 0) + { +#if USE_THREAD + _receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken))); + _receiveThread.Start(); +#else + tasks.Add(ReceiveVoiceAsync()); #endif - )}.Concat(base.Run()).ToArray(); + } + +#if !DNXCORE50 + tasks.Add(WatcherAsync()); #endif + tasks.AddRange(base.Run()); + + return tasks.ToArray(); } protected override Task Cleanup() { diff --git a/src/Discord.Net/WebSockets/WebSocket.cs b/src/Discord.Net/WebSockets/WebSocket.cs index a1bb06462..a50a07433 100644 --- a/src/Discord.Net/WebSockets/WebSocket.cs +++ b/src/Discord.Net/WebSockets/WebSocket.cs @@ -35,7 +35,7 @@ namespace Discord.WebSockets internal abstract partial class WebSocket { protected readonly IWebSocketEngine _engine; - protected readonly DiscordBaseClient _client; + protected readonly DiscordSimpleClient _client; protected readonly LogMessageSeverity _logLevel; protected readonly ManualResetEventSlim _connectedEvent; @@ -57,7 +57,7 @@ namespace Discord.WebSockets public WebSocketState State => (WebSocketState)_state; protected int _state; - public WebSocket(DiscordBaseClient client) + public WebSocket(DiscordSimpleClient client) { _client = client; _logLevel = client.Config.LogLevel;