@@ -136,15 +136,6 @@ | |||
<Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | |||
<Link>DiscordAPIClient.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordBaseClient.cs"> | |||
<Link>DiscordBaseClient.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordBaseClient.Events.cs"> | |||
<Link>DiscordBaseClient.Events.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordBaseClient.Voice.cs"> | |||
<Link>DiscordBaseClient.Voice.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordClient.API.cs"> | |||
<Link>DiscordClient.API.cs</Link> | |||
</Compile> | |||
@@ -160,6 +151,15 @@ | |||
<Compile Include="..\Discord.Net\DiscordClientConfig.cs"> | |||
<Link>DiscordClientConfig.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordSimpleClient.cs"> | |||
<Link>DiscordSimpleClient.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordSimpleClient.Events.cs"> | |||
<Link>DiscordSimpleClient.Events.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordSimpleClient.Voice.cs"> | |||
<Link>DiscordSimpleClient.Voice.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Enums\ChannelTypes.cs"> | |||
<Link>Enums\ChannelTypes.cs</Link> | |||
</Compile> | |||
@@ -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 | |||
{ | |||
/// <summary> Provides a connection to the DiscordApp service. </summary> | |||
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<Message> _pendingMessages; | |||
private readonly ConcurrentDictionary<string, DiscordSimpleClient> _voiceClients; | |||
/// <summary> Returns the current logged-in user. </summary> | |||
public User CurrentUser => _currentUser; | |||
@@ -52,6 +55,8 @@ namespace Discord | |||
_api = new DiscordAPIClient(_config.LogLevel, _config.APITimeout); | |||
if (_config.UseMessageQueue) | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
if (_config.EnableVoiceMultiserver) | |||
_voiceClients = new ConcurrentDictionary<string, DiscordSimpleClient>(); | |||
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; | |||
} | |||
/// <summary> Connects to the Discord server with the provided email and password. </summary> | |||
/// <returns> Returns a token for future connections. </returns> | |||
public new async Task<string> 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<VoiceServerUpdateEvent>(_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<IDiscordVoiceClient> 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(); | |||
} | |||
} | |||
} |
@@ -11,7 +11,7 @@ namespace Discord | |||
Both = Outgoing | Incoming | |||
} | |||
public class DiscordClientConfig | |||
public sealed class DiscordClientConfig | |||
{ | |||
/// <summary> 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. </summary> | |||
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; | |||
} | |||
} | |||
} |
@@ -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<DisconnectedEventArgs> VoiceDisconnected; | |||
private void RaiseVoiceDisconnected(DisconnectedEventArgs e) | |||
public event EventHandler<VoiceDisconnectedEventArgs> 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<VoicePacketEventArgs> OnVoicePacket; |
@@ -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 | |||
/// <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> | |||
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); | |||
} | |||
/// <summary> Clears the PCM buffer. </summary> | |||
public void ClearVoicePCM() | |||
void IDiscordVoiceClient.ClearVoicePCM() | |||
{ | |||
CheckReady(checkVoice: true); | |||
@@ -72,7 +78,7 @@ namespace Discord | |||
} | |||
/// <summary> Returns a task that completes once the voice output buffer is empty. </summary> | |||
public async Task WaitVoice() | |||
async Task IDiscordVoiceClient.WaitVoice() | |||
{ | |||
CheckReady(checkVoice: true); | |||
@@ -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 | |||
} | |||
/// <summary> Provides a barebones connection to the Discord service </summary> | |||
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; | |||
/// <summary> 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. </summary> | |||
public DiscordClientConfig Config => _config; | |||
protected readonly DiscordClientConfig _config; | |||
/// <summary> Returns the id of the current logged-in user. </summary> | |||
public string CurrentUserId => _currentUserId; | |||
private string _currentUserId; | |||
@@ -43,64 +45,78 @@ namespace Discord | |||
public DiscordClientState State => (DiscordClientState)_state; | |||
private int _state; | |||
/// <summary> 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. </summary> | |||
public DiscordClientConfig Config => _config; | |||
protected readonly DiscordClientConfig _config; | |||
public CancellationToken CancelToken => _cancelToken; | |||
private CancellationTokenSource _cancelTokenSource; | |||
private CancellationToken _cancelToken; | |||
/// <summary> Initializes a new instance of the DiscordClient class. </summary> | |||
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<string> Connect(string gateway, string token) | |||
public async Task<string> 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<string>("id"); | |||
switch (e.Type) | |||
{ | |||
case "READY": | |||
_currentUserId = e.Payload["user"].Value<string>("id"); | |||
break; | |||
case "VOICE_SERVER_UPDATE": | |||
{ | |||
string guildId = e.Payload.Value<string>("guild_id"); | |||
if (_enableVoice && guildId == _voiceSocket.CurrentServerId) | |||
{ | |||
string token = e.Payload.Value<string>("token"); | |||
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; | |||
return _voiceSocket.Login(_currentUserId, _dataSocket.SessionId, token, CancelToken); | |||
} | |||
} | |||
break; | |||
} | |||
return TaskHelper.CompletedTask; | |||
} | |||
} |
@@ -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) | |||
{ | |||
} | |||
@@ -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<Task> tasks = new List<Task>(); | |||
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() | |||
{ | |||
@@ -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; | |||