diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index 3155b01d9..64f5766b0 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -89,11 +89,11 @@ DiscordClientConfig.cs - - DiscordTextWebSocket.cs + + DiscordDataSocket.cs - - DiscordTextWebSocket.Events.cs + + DiscordDataSocket.Events.cs DiscordVoiceSocket.cs diff --git a/src/Discord.Net/DiscordClient.Events.cs b/src/Discord.Net/DiscordClient.Events.cs index 41a8c46d8..5b022b282 100644 --- a/src/Discord.Net/DiscordClient.Events.cs +++ b/src/Discord.Net/DiscordClient.Events.cs @@ -2,248 +2,310 @@ namespace Discord { - public partial class DiscordClient + public enum DebugMessageType : byte { - //Debug - public sealed class LogMessageEventArgs : EventArgs + Connection, + Event, + Cache, + WebSocketRawInput, + WebSocketUnknownInput, + WebSocketEvent, + WebSocketUnknownEvent, + VoiceOutput + } + public sealed class LogMessageEventArgs : EventArgs + { + public readonly DebugMessageType Type; + public readonly string Message; + internal LogMessageEventArgs(DebugMessageType type, string msg) { Type = type; Message = msg; } + } + public sealed class ServerEventArgs : EventArgs + { + public readonly Server Server; + internal ServerEventArgs(Server server) { Server = server; } + } + public sealed class ChannelEventArgs : EventArgs + { + public readonly Channel Channel; + internal ChannelEventArgs(Channel channel) { Channel = channel; } + } + public sealed class UserEventArgs : EventArgs + { + public readonly User User; + internal UserEventArgs(User user) { User = user; } + } + public sealed class MessageEventArgs : EventArgs + { + public readonly Message Message; + internal MessageEventArgs(Message msg) { Message = msg; } + } + public sealed class RoleEventArgs : EventArgs + { + public readonly Role Role; + internal RoleEventArgs(Role role) { Role = role; } + } + public sealed class BanEventArgs : EventArgs + { + public readonly User User; + public readonly Server Server; + internal BanEventArgs(User user, Server server) { - public readonly string Message; - internal LogMessageEventArgs(string msg) { Message = msg; } + User = user; + Server = server; } - public event EventHandler DebugMessage; - private void RaiseOnDebugMessage(string message) + } + public sealed class MemberEventArgs : EventArgs + { + public readonly Membership Member; + internal MemberEventArgs(Membership member) { Member = member; } + } + public sealed class UserTypingEventArgs : EventArgs + { + public readonly User User; + public readonly Channel Channel; + internal UserTypingEventArgs(User user, Channel channel) { - if (DebugMessage != null) - DebugMessage(this, new LogMessageEventArgs(message)); + User = user; + Channel = channel; } - public event EventHandler VoiceDebugMessage; - private void RaiseOnVoiceDebugMessage(string message) + } + public sealed class VoiceServerUpdatedEventArgs : EventArgs + { + public readonly Server Server; + public readonly string Endpoint; + internal VoiceServerUpdatedEventArgs(Server server, string endpoint) { - if (VoiceDebugMessage != null) - VoiceDebugMessage(this, new LogMessageEventArgs(message)); + Server = server; + Endpoint = endpoint; + } + } + + + public partial class DiscordClient + { + //Debug + public event EventHandler DebugMessage; + internal void RaiseOnDebugMessage(DebugMessageType type, string message) + { + if (DebugMessage != null) + DebugMessage(this, new LogMessageEventArgs(type, message)); } //General public event EventHandler Connected; private void RaiseConnected() { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"Connected"); if (Connected != null) Connected(this, EventArgs.Empty); } public event EventHandler Disconnected; private void RaiseDisconnected() { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"Disconnected"); if (Disconnected != null) Disconnected(this, EventArgs.Empty); } //Server - public sealed class ServerEventArgs : EventArgs - { - public readonly Server Server; - internal ServerEventArgs(Server server) { Server = server; } - } public event EventHandler ServerCreated; private void RaiseServerCreated(Server server) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ServerCreated {server.Name} ({server.Id})"); if (ServerCreated != null) ServerCreated(this, new ServerEventArgs(server)); } public event EventHandler ServerDestroyed; private void RaiseServerDestroyed(Server server) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ServerDestroyed {server.Name} ({server.Id})"); if (ServerDestroyed != null) ServerDestroyed(this, new ServerEventArgs(server)); } public event EventHandler ServerUpdated; private void RaiseServerUpdated(Server server) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ServerUpdated {server.Name} ({server.Id})"); if (ServerUpdated != null) ServerUpdated(this, new ServerEventArgs(server)); } //Channel - public sealed class ChannelEventArgs : EventArgs - { - public readonly Channel Channel; - internal ChannelEventArgs(Channel channel) { Channel = channel; } - } - public event EventHandler ChannelCreated; private void RaiseChannelCreated(Channel channel) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelCreated {channel.Name} ({channel.Id})"); if (ChannelCreated != null) ChannelCreated(this, new ChannelEventArgs(channel)); } public event EventHandler ChannelDestroyed; private void RaiseChannelDestroyed(Channel channel) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelDestroyed {channel.Name} ({channel.Id})"); if (ChannelDestroyed != null) ChannelDestroyed(this, new ChannelEventArgs(channel)); } public event EventHandler ChannelUpdated; private void RaiseChannelUpdated(Channel channel) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"ChannelUpdated {channel.Name} ({channel.Id})"); if (ChannelUpdated != null) ChannelUpdated(this, new ChannelEventArgs(channel)); } //User - public sealed class UserEventArgs : EventArgs - { - public readonly User User; - internal UserEventArgs(User user) { User = user; } - } public event EventHandler UserUpdated; private void RaiseUserUpdated(User user) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"UserUpdated {user.Name} ({user.Id})"); if (UserUpdated != null) UserUpdated(this, new UserEventArgs(user)); } //Message - public sealed class MessageEventArgs : EventArgs - { - public readonly Message Message; - internal MessageEventArgs(Message msg) { Message = msg; } - } - public event EventHandler MessageCreated; private void RaiseMessageCreated(Message msg) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MessageCreated {msg.Id}"); if (MessageCreated != null) MessageCreated(this, new MessageEventArgs(msg)); } public event EventHandler MessageDeleted; private void RaiseMessageDeleted(Message msg) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MessageDeleted {msg.Id}"); if (MessageDeleted != null) MessageDeleted(this, new MessageEventArgs(msg)); } public event EventHandler MessageUpdated; private void RaiseMessageUpdated(Message msg) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MessageUpdated {msg.Id}"); if (MessageUpdated != null) MessageUpdated(this, new MessageEventArgs(msg)); } public event EventHandler MessageRead; private void RaiseMessageRead(Message msg) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MessageRead {msg.Id}"); if (MessageRead != null) MessageRead(this, new MessageEventArgs(msg)); } public event EventHandler MessageSent; private void RaiseMessageSent(Message msg) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MessageSent {msg.Id}"); if (MessageSent != null) MessageSent(this, new MessageEventArgs(msg)); } //Role - public sealed class RoleEventArgs : EventArgs - { - public readonly Role Role; - internal RoleEventArgs(Role role) { Role = role; } - } - public event EventHandler RoleCreated; private void RaiseRoleCreated(Role role) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"RoleCreated {role.Name} ({role.Id})"); if (RoleCreated != null) RoleCreated(this, new RoleEventArgs(role)); } public event EventHandler RoleUpdated; private void RaiseRoleDeleted(Role role) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"RoleDeleted {role.Name} ({role.Id})"); if (RoleDeleted != null) RoleDeleted(this, new RoleEventArgs(role)); } public event EventHandler RoleDeleted; private void RaiseRoleUpdated(Role role) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"RoleUpdated {role.Name} ({role.Id})"); if (RoleUpdated != null) RoleUpdated(this, new RoleEventArgs(role)); } //Ban - public sealed class BanEventArgs : EventArgs - { - public readonly User User; - public readonly Server Server; - internal BanEventArgs(User user, Server server) - { - User = user; - Server = server; - } - } - public event EventHandler BanAdded; private void RaiseBanAdded(User user, Server server) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"BanAdded {user.Name} ({user.Id}) on {server.Name} ({server.Id})"); if (BanAdded != null) BanAdded(this, new BanEventArgs(user, server)); } public event EventHandler BanRemoved; private void RaiseBanRemoved(User user, Server server) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"BanRemoved {user.Name} ({user.Id}) on {server.Name} ({server.Id})"); if (BanRemoved != null) BanRemoved(this, new BanEventArgs(user, server)); } //Member - public sealed class MemberEventArgs : EventArgs - { - public readonly Membership Membership; - internal MemberEventArgs(Membership membership) { Membership = membership; } - } - public event EventHandler MemberAdded; - private void RaiseMemberAdded(Membership membership) + private void RaiseMemberAdded(Membership member) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MemberAdded {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); if (MemberAdded != null) - MemberAdded(this, new MemberEventArgs(membership)); + MemberAdded(this, new MemberEventArgs(member)); } public event EventHandler MemberRemoved; - private void RaiseMemberRemoved(Membership membership) + private void RaiseMemberRemoved(Membership member) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MemberRemoved {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); if (MemberRemoved != null) - MemberRemoved(this, new MemberEventArgs(membership)); + MemberRemoved(this, new MemberEventArgs(member)); } public event EventHandler MemberUpdated; - private void RaiseMemberUpdated(Membership membership) + private void RaiseMemberUpdated(Membership member) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"MemberUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); if (MemberUpdated != null) - MemberUpdated(this, new MemberEventArgs(membership)); + MemberUpdated(this, new MemberEventArgs(member)); } //Status - public sealed class UserTypingEventArgs : EventArgs - { - public readonly User User; - public readonly Channel Channel; - internal UserTypingEventArgs(User user, Channel channel) - { - User = user; - Channel = channel; - } - } - public event EventHandler PresenceUpdated; private void RaisePresenceUpdated(Membership member) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"PresenceUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); if (PresenceUpdated != null) PresenceUpdated(this, new MemberEventArgs(member)); } public event EventHandler VoiceStateUpdated; private void RaiseVoiceStateUpdated(Membership member) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceStateUpdated {member.User.Name} ({member.UserId}) on {member.Server.Name} ({member.ServerId})"); if (VoiceStateUpdated != null) VoiceStateUpdated(this, new MemberEventArgs(member)); } public event EventHandler UserTyping; private void RaiseUserTyping(User user, Channel channel) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceStateUpdated {user.Name} ({user.Id}) on {channel.Name} ({channel.Id})"); if (UserTyping != null) UserTyping(this, new UserTypingEventArgs(user, channel)); } @@ -252,29 +314,24 @@ namespace Discord public event EventHandler VoiceConnected; private void RaiseVoiceConnected() { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceConnected"); if (VoiceConnected != null) VoiceConnected(this, EventArgs.Empty); } public event EventHandler VoiceDisconnected; private void RaiseVoiceDisconnected() { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceDisconnected"); if (VoiceDisconnected != null) VoiceDisconnected(this, EventArgs.Empty); } - public sealed class VoiceServerUpdatedEventArgs : EventArgs - { - public readonly Server Server; - public readonly string Endpoint; - internal VoiceServerUpdatedEventArgs(Server server, string endpoint) - { - Server = server; - Endpoint = endpoint; - } - } - public event EventHandler VoiceServerUpdated; private void RaiseVoiceServerUpdated(Server server, string endpoint) { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Event, $"VoiceServerUpdated {server.Name} ({server.Id})"); if (VoiceServerUpdated != null) VoiceServerUpdated(this, new VoiceServerUpdatedEventArgs(server, endpoint)); } diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 1ea1ce479..e112e181b 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -17,8 +17,7 @@ namespace Discord /// Provides a connection to the DiscordApp service. public partial class DiscordClient { - private readonly DiscordClientConfig _config; - private readonly DiscordTextWebSocket _webSocket; + private readonly DiscordDataSocket _webSocket; #if !DNXCORE50 private readonly DiscordVoiceSocket _voiceWebSocket; #endif @@ -36,6 +35,9 @@ namespace Discord public string UserId { get; private set; } public string SessionId { get; private set; } + public DiscordClientConfig Config => _config; + private readonly DiscordClientConfig _config; + /// Returns a collection of all users the client can see across all servers. /// This collection does not guarantee any ordering. public IEnumerable Users => _users; @@ -113,7 +115,12 @@ namespace Discord }); _servers = new AsyncCache( - (key, parentKey) => new Server(key, this), + (key, parentKey) => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created server {key}."); + return new Server(key, this); + }, (server, model) => { server.Name = model.Name; @@ -141,12 +148,28 @@ namespace Discord foreach (var membership in extendedModel.Presences) server.UpdateMember(membership); } + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated server {server.Name} ({server.Id})."); }, - server => { } + server => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed server {server.Name} ({server.Id})."); + } ); _channels = new AsyncCache( - (key, parentKey) => new Channel(key, parentKey, this), + (key, parentKey) => + { + if (_config.EnableDebug) + { + if (parentKey != null) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created channel {key} in server {parentKey}."); + else + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created private channel {key}."); + } + return new Channel(key, parentKey, this); + }, (channel, model) => { channel.Name = model.Name; @@ -176,6 +199,13 @@ namespace Discord else channel.PermissionOverwrites = null; } + if (_config.EnableDebug) + { + if (channel.IsPrivate) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated private channel {channel.Name} ({channel.Id})."); + else + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId})."); + } }, channel => { @@ -184,10 +214,22 @@ namespace Discord var user = channel.Recipient; if (user.PrivateChannelId == channel.Id) user.PrivateChannelId = null; + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed private channel {channel.Name} ({channel.Id})."); + } + else + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed channel {channel.Name} ({channel.Id}) in server {channel.Server?.Name} ({channel.ServerId})."); } }); _messages = new AsyncCache( - (key, parentKey) => new Message(key, parentKey, this), + (key, parentKey) => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created message {key} in channel {parentKey}."); + return new Message(key, parentKey, this); + }, (message, model) => { if (model is API.Models.Message) @@ -260,21 +302,44 @@ namespace Discord if (extendedModel.Author != null) message.UserId = extendedModel.Author.Id; } + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId})."); }, - message => { } + message => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed message {message.Id} in channel {message.Channel?.Name} ({message.ChannelId})."); + } ); - _pendingMessages = new ConcurrentQueue(); + if (_config.UseMessageQueue) + _pendingMessages = new ConcurrentQueue(); _roles = new AsyncCache( - (key, parentKey) => new Role(key, parentKey, this), + (key, parentKey) => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created role {key} in server {parentKey}."); + return new Role(key, parentKey, this); + }, (role, model) => { role.Name = model.Name; role.Permissions.RawValue = (uint)model.Permissions; + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId})."); }, - role => { } + role => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed role {role.Name} ({role.Id}) in server {role.Server?.Name} ({role.ServerId})."); + } ); _users = new AsyncCache( - (key, parentKey) => new User(key, this), + (key, parentKey) => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key}."); + return new User(key, this); + }, (user, model) => { user.AvatarId = model.Avatar; @@ -286,53 +351,94 @@ namespace Discord user.Email = extendedModel.Email; user.IsVerified = extendedModel.IsVerified; } + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {user?.Name} ({user.Id})."); }, - user => { } + user => + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {user?.Name} ({user.Id})."); + } ); - _webSocket = new DiscordTextWebSocket(this, _config.ConnectionTimeout, _config.WebSocketInterval); + _webSocket = new DiscordDataSocket(this, _config.ConnectionTimeout, _config.WebSocketInterval); _webSocket.Connected += (s, e) => RaiseConnected(); - _webSocket.Disconnected += async (s, e) => + _webSocket.Disconnected += async (s, e) => { - //Reconnect if we didn't cause the disconnect + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket disconnected."); RaiseDisconnected(); + + //Reconnect if we didn't cause the disconnect while (!_disconnectToken.IsCancellationRequested) { try { await Task.Delay(_config.ReconnectDelay); await _webSocket.ReconnectAsync(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); await _webSocket.Login(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); break; } catch (Exception ex) { - RaiseOnDebugMessage($"Reconnect Failed: {ex.Message}"); + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket reconnect failed: {ex.Message}"); //Net is down? We can keep trying to reconnect until the user runs Disconnect() await Task.Delay(_config.FailedReconnectDelay); } } }; - _webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Message); + if (_config.EnableDebug) + _webSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, e.Message); #if !DNXCORE50 if (_config.EnableVoice) { _voiceWebSocket = new DiscordVoiceSocket(this, _config.VoiceConnectionTimeout, _config.WebSocketInterval); _voiceWebSocket.Connected += (s, e) => RaiseVoiceConnected(); - _voiceWebSocket.Disconnected += (s, e) => + _voiceWebSocket.Disconnected += async (s, e) => { - //TODO: Reconnect if we didn't cause the disconnect - RaiseVoiceDisconnected(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket disconnected."); + RaiseVoiceDisconnected(); + + //Reconnect if we didn't cause the disconnect + while (!_disconnectToken.IsCancellationRequested) + { + try + { + await Task.Delay(_config.ReconnectDelay); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket connected."); + await _voiceWebSocket.ReconnectAsync(); + break; + } + catch (Exception ex) + { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"VoiceSocket reconnect failed: {ex.Message}"); + //Net is down? We can keep trying to reconnect until the user runs Disconnect() + await Task.Delay(_config.FailedReconnectDelay); + } + } }; - _voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnVoiceDebugMessage(e.Message); + if (_config.EnableDebug) + _voiceWebSocket.OnDebugMessage += (s, e) => RaiseOnDebugMessage(e.Type, e.Message); } #endif -#pragma warning disable CS1998 //Disable unused async keyword warning +#if !DNXCORE50 _webSocket.GotEvent += async (s, e) => +#else + _webSocket.GotEvent += (s, e) => +#endif { - switch (e.Type) + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.WebSocketEvent, $"{e.Type}: {e.Event}"); + switch (e.Type) { //Global case "READY": //Resync @@ -610,12 +716,11 @@ namespace Discord //Others default: - RaiseOnDebugMessage("Unknown WebSocket message type: " + e.Type); + RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownEvent, "Unknown WebSocket message type: " + e.Type); break; } }; } -#pragma warning restore CS1998 //Restore unused async keyword warning private async Task SendAsync() { @@ -658,7 +763,7 @@ namespace Discord { await Task.Delay(-1, cancelToken); } - catch { } + catch (OperationCanceledException) { } } /// Returns the user with the specified id, or null if none was found. @@ -867,12 +972,19 @@ namespace Discord try { await _webSocket.ConnectAsync(url); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); Http.Token = token; + await _webSocket.Login(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); success = true; } catch (InvalidOperationException) //Bad Token { + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket had a bad token."); if (password == null) //If we don't have an alternate login, throw this error throw; } @@ -881,35 +993,51 @@ namespace Discord { //Open websocket while we wait for login response Task socketTask = _webSocket.ConnectAsync(url); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); + var response = await DiscordAPI.Login(emailOrUsername, password); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket got token."); + await socketTask; //Wait for websocket to finish connecting, then send token token = response.Token; Http.Token = token; await _webSocket.Login(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); success = true; } if (!success && password == null) //Anonymous login { //Open websocket while we wait for login response Task socketTask = _webSocket.ConnectAsync(url); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket connected."); + var response = await DiscordAPI.LoginAnonymous(emailOrUsername); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket generated anonymous token."); + await socketTask; //Wait for websocket to finish connecting, then send token token = response.Token; Http.Token = token; await _webSocket.Login(); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.Connection, $"DataSocket logged in."); success = true; } if (success) { var cancelToken = _disconnectToken.Token; if (_config.UseMessageQueue) - _tasks = Task.WhenAll(await Task.Factory.StartNew(SendAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); + _tasks = SendAsync(); else - _tasks = Task.WhenAll(await Task.Factory.StartNew(EmptyAsync, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)); + _tasks = EmptyAsync(); _tasks = _tasks.ContinueWith(async x => { await _webSocket.DisconnectAsync(); @@ -1347,10 +1475,13 @@ namespace Discord /// Number of bytes in this frame. public void SendVoicePCM(byte[] data, int count) { - if (!_config.EnableVoice) + if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client."); + if (count == 0) return; - _voiceWebSocket.SendPCMFrame(data, count); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Queued {count} bytes for voice output."); + _voiceWebSocket.SendPCMFrame(data, count); } /// Clears the PCM buffer. @@ -1359,6 +1490,8 @@ namespace Discord if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client."); + if (_config.EnableDebug) + RaiseOnDebugMessage(DebugMessageType.VoiceOutput, $"Cleared the voice buffer."); _voiceWebSocket.ClearPCMFrames(); } #endif diff --git a/src/Discord.Net/DiscordClientConfig.cs b/src/Discord.Net/DiscordClientConfig.cs index f97405657..a90316623 100644 --- a/src/Discord.Net/DiscordClientConfig.cs +++ b/src/Discord.Net/DiscordClientConfig.cs @@ -7,6 +7,9 @@ /// This option requires the opus .dll or .so be in the local lib/ folder. public bool EnableVoice { get; set; } = false; #endif + /// Enables the verbose DebugMessage event handler. May hinder performance but should help debug any issues. + public bool EnableDebug { get; set; } = false; + /// Max time in milliseconds to wait for the web socket to connect. public int ConnectionTimeout { get; set; } = 5000; /// Max time in milliseconds to wait for the voice web socket to connect. diff --git a/src/Discord.Net/DiscordTextWebSocket.Events.cs b/src/Discord.Net/DiscordDataSocket.Events.cs similarity index 91% rename from src/Discord.Net/DiscordTextWebSocket.Events.cs rename to src/Discord.Net/DiscordDataSocket.Events.cs index d7cbb227d..f0d8188c2 100644 --- a/src/Discord.Net/DiscordTextWebSocket.Events.cs +++ b/src/Discord.Net/DiscordDataSocket.Events.cs @@ -3,7 +3,7 @@ using System; namespace Discord { - internal partial class DiscordTextWebSocket + internal partial class DiscordDataSocket { public event EventHandler GotEvent; public sealed class MessageEventArgs : EventArgs diff --git a/src/Discord.Net/DiscordTextWebSocket.cs b/src/Discord.Net/DiscordDataSocket.cs similarity index 91% rename from src/Discord.Net/DiscordTextWebSocket.cs rename to src/Discord.Net/DiscordDataSocket.cs index bd85cc996..35c067c40 100644 --- a/src/Discord.Net/DiscordTextWebSocket.cs +++ b/src/Discord.Net/DiscordDataSocket.cs @@ -9,11 +9,11 @@ using WebSocketMessage = Discord.API.Models.TextWebSocketCommands.WebSocketMessa namespace Discord { - internal sealed partial class DiscordTextWebSocket : DiscordWebSocket + internal sealed partial class DiscordDataSocket : DiscordWebSocket { private ManualResetEventSlim _connectWaitOnLogin, _connectWaitOnLogin2; - public DiscordTextWebSocket(DiscordClient client, int timeout, int interval) + public DiscordDataSocket(DiscordClient client, int timeout, int interval) : base(client, timeout, interval) { _connectWaitOnLogin = new ManualResetEventSlim(false); @@ -72,7 +72,7 @@ namespace Discord } break; default: - RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); + RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownInput, "Unknown DataSocket op: " + msg.Operation); break; } #if DNXCORE diff --git a/src/Discord.Net/DiscordVoiceSocket.cs b/src/Discord.Net/DiscordVoiceSocket.cs index 323985ec3..38bb7621b 100644 --- a/src/Discord.Net/DiscordVoiceSocket.cs +++ b/src/Discord.Net/DiscordVoiceSocket.cs @@ -287,7 +287,7 @@ namespace Discord break; #endif default: - RaiseOnDebugMessage("Unknown WebSocket operation ID: " + msg.Operation); + RaiseOnDebugMessage(DebugMessageType.WebSocketUnknownInput, "Unknown VoiceSocket op: " + msg.Operation); break; } #if DNXCORE50 diff --git a/src/Discord.Net/DiscordWebSocket.Events.cs b/src/Discord.Net/DiscordWebSocket.Events.cs index c6d14ef90..005fe37d1 100644 --- a/src/Discord.Net/DiscordWebSocket.Events.cs +++ b/src/Discord.Net/DiscordWebSocket.Events.cs @@ -4,25 +4,26 @@ namespace Discord { internal abstract partial class DiscordWebSocket { + //Debug + public event EventHandler OnDebugMessage; + protected void RaiseOnDebugMessage(DebugMessageType type, string message) + { + if (OnDebugMessage != null) + OnDebugMessage(this, new LogMessageEventArgs(type, message)); + } + + //Connection public event EventHandler Connected; private void RaiseConnected() { if (Connected != null) Connected(this, EventArgs.Empty); } - public event EventHandler Disconnected; private void RaiseDisconnected() { if (Disconnected != null) Disconnected(this, EventArgs.Empty); } - - public event EventHandler OnDebugMessage; - protected void RaiseOnDebugMessage(string message) - { - if (OnDebugMessage != null) - OnDebugMessage(this, new DiscordClient.LogMessageEventArgs(message)); - } } } diff --git a/src/Discord.Net/Server.cs b/src/Discord.Net/Server.cs index bcab3629d..8e235376e 100644 --- a/src/Discord.Net/Server.cs +++ b/src/Discord.Net/Server.cs @@ -67,7 +67,12 @@ namespace Discord _client = client; _bans = new ConcurrentDictionary(); _members = new AsyncCache( - (key, parentKey) => new Membership(parentKey, key, _client), + (key, parentKey) => + { + if (_client.Config.EnableDebug) + _client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Created user {key} in server {parentKey}."); + return new Membership(parentKey, key, _client); + }, (member, model) => { if (model is API.Models.PresenceMemberInfo) @@ -103,7 +108,14 @@ namespace Discord member.IsDeafened = extendedModel.IsDeafened; member.IsMuted = extendedModel.IsMuted; } - } + if (_client.Config.EnableDebug) + _client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Updated user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId})."); + }, + (member) => + { + if (_client.Config.EnableDebug) + _client.RaiseOnDebugMessage(DebugMessageType.Cache, $"Destroyed user {member.User?.Name} ({member.UserId}) in server {member.Server?.Name} ({member.ServerId})."); + } ); }