Browse Source

Reworked event system, fixed presence and voice state updates

tags/1.0-rc
RogueException 9 years ago
parent
commit
5f18d39011
8 changed files with 242 additions and 160 deletions
  1. +17
    -11
      src/Discord.Net/API/DiscordAPIClient.cs
  2. +12
    -5
      src/Discord.Net/DiscordClient.cs
  3. +128
    -55
      src/Discord.Net/DiscordSocketClient.cs
  4. +0
    -2
      src/Discord.Net/DiscordSocketConfig.cs
  5. +2
    -22
      src/Discord.Net/Entities/WebSocket/CachedGuild.cs
  6. +0
    -56
      src/Discord.Net/Extensions/EventExtensions.cs
  7. +9
    -9
      src/Discord.Net/Logging/LogManager.cs
  8. +74
    -0
      src/Discord.Net/Utilities/AsyncEvent.cs

+ 17
- 11
src/Discord.Net/API/DiscordAPIClient.cs View File

@@ -1,6 +1,5 @@
using Discord.API.Gateway;
using Discord.API.Rest;
using Discord.Extensions;
using Discord.Net;
using Discord.Net.Converters;
using Discord.Net.Queue;
@@ -24,10 +23,17 @@ namespace Discord.API
{
public class DiscordApiClient : IDisposable
{
public event Func<string, string, double, Task> SentRequest;
public event Func<int, Task> SentGatewayMessage;
public event Func<GatewayOpCode, int?, string, object, Task> ReceivedGatewayEvent;
public event Func<Exception, Task> Disconnected;
private object _eventLock = new object();

public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
public event Func<int, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<int, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<int, Task>>();

public event Func<GatewayOpCode, int?, string, object, Task> ReceivedGatewayEvent { add { _receivedGatewayEvent.Add(value); } remove { _receivedGatewayEvent.Remove(value); } }
private readonly AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>> _receivedGatewayEvent = new AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>>();
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();

private readonly RequestQueue _requestQueue;
private readonly JsonSerializer _serializer;
@@ -67,19 +73,19 @@ namespace Discord.API
using (var reader = new StreamReader(decompressed))
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(reader.ReadToEnd());
await ReceivedGatewayEvent.RaiseAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
}
}
};
_gatewayClient.TextMessage += async text =>
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(text);
await ReceivedGatewayEvent.RaiseAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
};
_gatewayClient.Closed += async ex =>
{
await DisconnectAsync().ConfigureAwait(false);
await Disconnected.RaiseAsync(ex).ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
};
}

@@ -311,7 +317,7 @@ namespace Discord.API
stopwatch.Stop();

double milliseconds = ToMilliseconds(stopwatch);
await SentRequest.RaiseAsync(method, endpoint, milliseconds).ConfigureAwait(false);
await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false);

return responseStream;
}
@@ -324,7 +330,7 @@ namespace Discord.API
stopwatch.Stop();

double milliseconds = ToMilliseconds(stopwatch);
await SentRequest.RaiseAsync(method, endpoint, milliseconds).ConfigureAwait(false);
await _sentRequestEvent.InvokeAsync(method, endpoint, milliseconds).ConfigureAwait(false);

return responseStream;
}
@@ -344,7 +350,7 @@ namespace Discord.API
if (payload != null)
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
await _requestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
await SentGatewayMessage.RaiseAsync((int)opCode).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync((int)opCode).ConfigureAwait(false);
}

//Auth


+ 12
- 5
src/Discord.Net/DiscordClient.cs View File

@@ -15,8 +15,15 @@ namespace Discord
{
public class DiscordClient : IDiscordClient
{
public event Func<LogMessage, Task> Log;
public event Func<Task> LoggedIn, LoggedOut;
private readonly object _eventLock = new object();

public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
private readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();

public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>();
public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>();

internal readonly Logger _discordLogger, _restLogger, _queueLogger;
internal readonly SemaphoreSlim _connectionLock;
@@ -35,7 +42,7 @@ namespace Discord
public DiscordClient(DiscordConfig config)
{
_log = new LogManager(config.LogLevel);
_log.Message += async msg => await Log.RaiseAsync(msg).ConfigureAwait(false);
_log.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
_discordLogger = _log.CreateLogger("Discord");
_restLogger = _log.CreateLogger("Rest");
_queueLogger = _log.CreateLogger("Queue");
@@ -96,7 +103,7 @@ namespace Discord
throw;
}

await LoggedIn.RaiseAsync().ConfigureAwait(false);
await _loggedInEvent.InvokeAsync().ConfigureAwait(false);
}
protected virtual Task OnLoginAsync() => Task.CompletedTask;

@@ -123,7 +130,7 @@ namespace Discord

LoginState = LoginState.LoggedOut;

await LoggedOut.RaiseAsync().ConfigureAwait(false);
await _loggedOutEvent.InvokeAsync().ConfigureAwait(false);
}
protected virtual Task OnLogoutAsync() => Task.CompletedTask;



+ 128
- 55
src/Discord.Net/DiscordSocketClient.cs View File

@@ -21,22 +21,74 @@ namespace Discord
//TODO: Add resume logic
public class DiscordSocketClient : DiscordClient, IDiscordClient
{
public event Func<Task> Connected, Disconnected, Ready;
//public event Func<Channel> VoiceConnected, VoiceDisconnected;
public event Func<IChannel, Task> ChannelCreated, ChannelDestroyed;
public event Func<IChannel, IChannel, Task> ChannelUpdated;
public event Func<IMessage, Task> MessageReceived, MessageDeleted;
public event Func<IMessage, IMessage, Task> MessageUpdated;
public event Func<IRole, Task> RoleCreated, RoleDeleted;
public event Func<IRole, IRole, Task> RoleUpdated;
public event Func<IGuild, Task> JoinedGuild, LeftGuild, GuildAvailable, GuildUnavailable, GuildDownloadedMembers;
public event Func<IGuild, IGuild, Task> GuildUpdated;
public event Func<IUser, Task> UserJoined, UserLeft, UserBanned, UserUnbanned;
public event Func<IUser, IUser, Task> UserUpdated;
public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated;
public event Func<IChannel, IUser, Task> UserIsTyping;
public event Func<int, Task> LatencyUpdated;
//TODO: Add PresenceUpdated? VoiceStateUpdated?
private object _eventLock = new object();

//General
public event Func<Task> Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _connectedEvent = new AsyncEvent<Func<Task>>();
public event Func<Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _disconnectedEvent = new AsyncEvent<Func<Task>>();
public event Func<Task> Ready { add { _readyEvent.Add(value); } remove { _readyEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _readyEvent = new AsyncEvent<Func<Task>>();

public event Func<int, int, Task> LatencyUpdated { add { _latencyUpdatedEvent.Add(value); } remove { _latencyUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();

//Channels
public event Func<IChannel, Task> ChannelCreated { add { _channelCreatedEvent.Add(value); } remove { _channelCreatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IChannel, Task>> _channelCreatedEvent = new AsyncEvent<Func<IChannel, Task>>();
public event Func<IChannel, Task> ChannelDestroyed { add { _channelDestroyedEvent.Add(value); } remove { _channelDestroyedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IChannel, Task>> _channelDestroyedEvent = new AsyncEvent<Func<IChannel, Task>>();
public event Func<IChannel, IChannel, Task> ChannelUpdated { add { _channelUpdatedEvent.Add(value); } remove { _channelUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IChannel, IChannel, Task>> _channelUpdatedEvent = new AsyncEvent<Func<IChannel, IChannel, Task>>();

//Messages
public event Func<IMessage, Task> MessageReceived { add { _messageReceivedEvent.Add(value); } remove { _messageReceivedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IMessage, Task>> _messageReceivedEvent = new AsyncEvent<Func<IMessage, Task>>();
public event Func<ulong, Optional<IMessage>, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } }
private readonly AsyncEvent<Func<ulong, Optional<IMessage>, Task>> _messageDeletedEvent = new AsyncEvent<Func<ulong, Optional<IMessage>, Task>>();
public event Func<Optional<IMessage>, IMessage, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Optional<IMessage>, IMessage, Task>> _messageUpdatedEvent = new AsyncEvent<Func<Optional<IMessage>, IMessage, Task>>();

//Roles
public event Func<IRole, Task> RoleCreated { add { _roleCreatedEvent.Add(value); } remove { _roleCreatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IRole, Task>> _roleCreatedEvent = new AsyncEvent<Func<IRole, Task>>();
public event Func<IRole, Task> RoleDeleted { add { _roleDeletedEvent.Add(value); } remove { _roleDeletedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IRole, Task>> _roleDeletedEvent = new AsyncEvent<Func<IRole, Task>>();
public event Func<IRole, IRole, Task> RoleUpdated { add { _roleUpdatedEvent.Add(value); } remove { _roleUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IRole, IRole, Task>> _roleUpdatedEvent = new AsyncEvent<Func<IRole, IRole, Task>>();

//Guilds
public event Func<IGuild, Task> JoinedGuild { add { _joinedGuildEvent.Add(value); } remove { _joinedGuildEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, Task>> _joinedGuildEvent = new AsyncEvent<Func<IGuild, Task>>();
public event Func<IGuild, Task> LeftGuild { add { _leftGuildEvent.Add(value); } remove { _leftGuildEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, Task>> _leftGuildEvent = new AsyncEvent<Func<IGuild, Task>>();
public event Func<IGuild, Task> GuildAvailable { add { _guildAvailableEvent.Add(value); } remove { _guildAvailableEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, Task>> _guildAvailableEvent = new AsyncEvent<Func<IGuild, Task>>();
public event Func<IGuild, Task> GuildUnavailable { add { _guildUnavailableEvent.Add(value); } remove { _guildUnavailableEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, Task>> _guildUnavailableEvent = new AsyncEvent<Func<IGuild, Task>>();
public event Func<IGuild, Task> GuildDownloadedMembers { add { _guildDownloadedMembersEvent.Add(value); } remove { _guildDownloadedMembersEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, Task>> _guildDownloadedMembersEvent = new AsyncEvent<Func<IGuild, Task>>();
public event Func<IGuild, IGuild, Task> GuildUpdated { add { _guildUpdatedEvent.Add(value); } remove { _guildUpdatedEvent.Remove(value); } }
private AsyncEvent<Func<IGuild, IGuild, Task>> _guildUpdatedEvent = new AsyncEvent<Func<IGuild, IGuild, Task>>();

//Users
public event Func<IGuildUser, Task> UserJoined { add { _userJoinedEvent.Add(value); } remove { _userJoinedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IGuildUser, Task>> _userJoinedEvent = new AsyncEvent<Func<IGuildUser, Task>>();
public event Func<IGuildUser, Task> UserLeft { add { _userLeftEvent.Add(value); } remove { _userLeftEvent.Remove(value); } }
private readonly AsyncEvent<Func<IGuildUser, Task>> _userLeftEvent = new AsyncEvent<Func<IGuildUser, Task>>();
public event Func<IUser, IGuild, Task> UserBanned { add { _userBannedEvent.Add(value); } remove { _userBannedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userBannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>();
public event Func<IUser, IGuild, Task> UserUnbanned { add { _userUnbannedEvent.Add(value); } remove { _userUnbannedEvent.Remove(value); } }
private readonly AsyncEvent<Func<IUser, IGuild, Task>> _userUnbannedEvent = new AsyncEvent<Func<IUser, IGuild, Task>>();
public event Func<Optional<IGuildUser>, IGuildUser, Task> UserUpdated { add { _userUpdatedEvent.Add(value); } remove { _userUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Optional<IGuildUser>, IGuildUser, Task>> _userUpdatedEvent = new AsyncEvent<Func<Optional<IGuildUser>, IGuildUser, Task>>();
public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated { add { _selfUpdatedEvent.Add(value); } remove { _selfUpdatedEvent.Remove(value); } }
private readonly AsyncEvent<Func<ISelfUser, ISelfUser, Task>> _selfUpdatedEvent = new AsyncEvent<Func<ISelfUser, ISelfUser, Task>>();
public event Func<IUser, IChannel, Task> UserIsTyping { add { _userIsTypingEvent.Add(value); } remove { _userIsTypingEvent.Remove(value); } }
private readonly AsyncEvent<Func<IUser, IChannel, Task>> _userIsTypingEvent = new AsyncEvent<Func<IUser, IChannel, Task>>();

//TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected;

private readonly ConcurrentQueue<ulong> _largeGuilds;
private readonly Logger _gatewayLogger;
@@ -46,7 +98,6 @@ namespace Discord
private readonly DataStoreProvider _dataStoreProvider;
private readonly JsonSerializer _serializer;
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
private readonly bool _enablePreUpdateEvents;
private readonly int _largeThreshold;
private readonly int _totalShards;

@@ -78,8 +129,7 @@ namespace Discord
internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();

/// <summary> Creates a new REST/WebSocket discord client. </summary>
public DiscordSocketClient()
: this(new DiscordSocketConfig()) { }
public DiscordSocketClient() : this(new DiscordSocketConfig()) { }
/// <summary> Creates a new REST/WebSocket discord client. </summary>
public DiscordSocketClient(DiscordSocketConfig config)
: base(config)
@@ -93,7 +143,6 @@ namespace Discord
_dataStoreProvider = config.DataStoreProvider;

MessageCacheSize = config.MessageCacheSize;
_enablePreUpdateEvents = config.EnablePreUpdateEvents;
_largeThreshold = config.LargeThreshold;
_gatewayLogger = _log.CreateLogger("Gateway");
@@ -173,7 +222,7 @@ namespace Discord
_connectTask = new TaskCompletionSource<bool>();
_cancelToken = new CancellationTokenSource();
await ApiClient.ConnectAsync().ConfigureAwait(false);
await Connected.RaiseAsync().ConfigureAwait(false);
await _connectedEvent.InvokeAsync().ConfigureAwait(false);

await _connectTask.Task.ConfigureAwait(false);
@@ -228,7 +277,7 @@ namespace Discord
ConnectionState = ConnectionState.Disconnected;
await _gatewayLogger.InfoAsync("Disconnected").ConfigureAwait(false);

await Disconnected.RaiseAsync().ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync().ConfigureAwait(false);
}
private async Task StartReconnectAsync()
{
@@ -463,12 +512,14 @@ namespace Discord
var heartbeatTime = _heartbeatTime;
if (heartbeatTime != 0)
{
var latency = (int)(Environment.TickCount - _heartbeatTime);
int latency = (int)(Environment.TickCount - _heartbeatTime);
_heartbeatTime = 0;
await _gatewayLogger.VerboseAsync($"Latency = {latency} ms").ConfigureAwait(false);

int before = Latency;
Latency = latency;

await LatencyUpdated.RaiseAsync(latency).ConfigureAwait(false);
await _latencyUpdatedEvent.InvokeAsync(before, latency).ConfigureAwait(false);
}
}
break;
@@ -523,7 +574,7 @@ namespace Discord

_guildDownloadTask = WaitForGuildsAsync(_cancelToken.Token);

await Ready.RaiseAsync().ConfigureAwait(false);
await _readyEvent.InvokeAsync().ConfigureAwait(false);

_connectTask.TrySetResult(true); //Signal the .Connect() call to complete
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
@@ -546,7 +597,7 @@ namespace Discord
if (data.Unavailable != false)
{
guild = AddGuild(data, DataStore);
await JoinedGuild.RaiseAsync(guild).ConfigureAwait(false);
await _joinedGuildEvent.InvokeAsync(guild).ConfigureAwait(false);
await _gatewayLogger.InfoAsync($"Joined {data.Name}").ConfigureAwait(false);
}
else
@@ -568,7 +619,7 @@ namespace Discord
if (data.Unavailable != true)
{
await _gatewayLogger.VerboseAsync($"Connected to {data.Name}").ConfigureAwait(false);
await GuildAvailable.RaiseAsync(guild).ConfigureAwait(false);
await _guildAvailableEvent.InvokeAsync(guild).ConfigureAwait(false);
}
}
break;
@@ -580,9 +631,9 @@ namespace Discord
var guild = DataStore.GetGuild(data.Id);
if (guild != null)
{
var before = _enablePreUpdateEvents ? guild.Clone() : null;
var before = guild.Clone();
guild.Update(data, UpdateSource.WebSocket);
await GuildUpdated.RaiseAsync(before, guild).ConfigureAwait(false);
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
}
else
{
@@ -604,11 +655,11 @@ namespace Discord
foreach (var member in guild.Members)
member.User.RemoveRef(this);

await GuildUnavailable.RaiseAsync(guild).ConfigureAwait(false);
await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false);
await _gatewayLogger.VerboseAsync($"Disconnected from {data.Name}").ConfigureAwait(false);
if (data.Unavailable != true)
{
await LeftGuild.RaiseAsync(guild).ConfigureAwait(false);
await _leftGuildEvent.InvokeAsync(guild).ConfigureAwait(false);
await _gatewayLogger.InfoAsync($"Left {data.Name}").ConfigureAwait(false);
}
else
@@ -644,7 +695,7 @@ namespace Discord
else
channel = AddDMChannel(data, DataStore);
if (channel != null)
await ChannelCreated.RaiseAsync(channel).ConfigureAwait(false);
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
}
break;
case "CHANNEL_UPDATE":
@@ -655,9 +706,9 @@ namespace Discord
var channel = DataStore.GetChannel(data.Id);
if (channel != null)
{
var before = _enablePreUpdateEvents ? channel.Clone() : null;
var before = channel.Clone();
channel.Update(data, UpdateSource.WebSocket);
await ChannelUpdated.RaiseAsync(before, channel).ConfigureAwait(false);
await _channelUpdatedEvent.InvokeAsync(before, channel).ConfigureAwait(false);
}
else
{
@@ -686,7 +737,7 @@ namespace Discord
else
channel = RemoveDMChannel(data.Recipient.Value.Id);
if (channel != null)
await ChannelDestroyed.RaiseAsync(channel).ConfigureAwait(false);
await _channelDestroyedEvent.InvokeAsync(channel).ConfigureAwait(false);
else
{
await _gatewayLogger.WarningAsync("CHANNEL_DELETE referenced an unknown channel.").ConfigureAwait(false);
@@ -705,7 +756,7 @@ namespace Discord
if (guild != null)
{
var user = guild.AddUser(data, DataStore);
await UserJoined.RaiseAsync(user).ConfigureAwait(false);
await _userJoinedEvent.InvokeAsync(user).ConfigureAwait(false);
}
else
{
@@ -725,9 +776,9 @@ namespace Discord
var user = guild.GetUser(data.User.Id);
if (user != null)
{
var before = _enablePreUpdateEvents ? user.Clone() : null;
var before = user.Clone();
user.Update(data, UpdateSource.WebSocket);
await UserUpdated.RaiseAsync(before, user).ConfigureAwait(false);
await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false);
}
else
{
@@ -754,7 +805,7 @@ namespace Discord
if (user != null)
{
user.User.RemoveRef(this);
await UserLeft.RaiseAsync(user).ConfigureAwait(false);
await _userLeftEvent.InvokeAsync(user).ConfigureAwait(false);
}
else
{
@@ -783,7 +834,7 @@ namespace Discord
if (guild.DownloadedMemberCount >= guild.MemberCount) //Finished downloading for there
{
guild.CompleteDownloadMembers();
await GuildDownloadedMembers.RaiseAsync(guild).ConfigureAwait(false);
await _guildDownloadedMembersEvent.InvokeAsync(guild).ConfigureAwait(false);
}
}
else
@@ -804,7 +855,7 @@ namespace Discord
if (guild != null)
{
var role = guild.AddRole(data.Role);
await RoleCreated.RaiseAsync(role).ConfigureAwait(false);
await _roleCreatedEvent.InvokeAsync(role).ConfigureAwait(false);
}
else
{
@@ -824,9 +875,9 @@ namespace Discord
var role = guild.GetRole(data.Role.Id);
if (role != null)
{
var before = _enablePreUpdateEvents ? role.Clone() : null;
var before = role.Clone();
role.Update(data.Role, UpdateSource.WebSocket);
await RoleUpdated.RaiseAsync(before, role).ConfigureAwait(false);
await _roleUpdatedEvent.InvokeAsync(before, role).ConfigureAwait(false);
}
else
{
@@ -851,7 +902,7 @@ namespace Discord
{
var role = guild.RemoveRole(data.RoleId);
if (role != null)
await RoleDeleted.RaiseAsync(role).ConfigureAwait(false);
await _roleDeletedEvent.InvokeAsync(role).ConfigureAwait(false);
else
{
await _gatewayLogger.WarningAsync("GUILD_ROLE_DELETE referenced an unknown role.").ConfigureAwait(false);
@@ -874,7 +925,7 @@ namespace Discord
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
await UserBanned.RaiseAsync(new User(data.User)).ConfigureAwait(false);
await _userBannedEvent.InvokeAsync(new User(data.User), guild).ConfigureAwait(false);
else
{
await _gatewayLogger.WarningAsync("GUILD_BAN_ADD referenced an unknown guild.").ConfigureAwait(false);
@@ -889,7 +940,7 @@ namespace Discord
var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
await UserUnbanned.RaiseAsync(new User(data.User)).ConfigureAwait(false);
await _userUnbannedEvent.InvokeAsync(new User(data.User), guild).ConfigureAwait(false);
else
{
await _gatewayLogger.WarningAsync("GUILD_BAN_REMOVE referenced an unknown guild.").ConfigureAwait(false);
@@ -912,7 +963,7 @@ namespace Discord
if (author != null)
{
var msg = channel.AddMessage(author, data);
await MessageReceived.RaiseAsync(msg).ConfigureAwait(false);
await _messageReceivedEvent.InvokeAsync(msg).ConfigureAwait(false);
}
else
{
@@ -939,7 +990,7 @@ namespace Discord
CachedMessage cachedMsg = channel.GetMessage(data.Id);
if (cachedMsg != null)
{
before = _enablePreUpdateEvents ? cachedMsg.Clone() : null;
before = cachedMsg.Clone();
cachedMsg.Update(data, UpdateSource.WebSocket);
after = cachedMsg;
}
@@ -951,7 +1002,12 @@ namespace Discord
after = new Message(channel, author, data);
}
if (after != null)
await MessageUpdated.RaiseAsync(before, after).ConfigureAwait(false);
await _messageUpdatedEvent.InvokeAsync(Optional.Create(before), after).ConfigureAwait(false);
else
{
await _gatewayLogger.WarningAsync("MESSAGE_UPDATE was unable to build an updated message.").ConfigureAwait(false);
return;
}
}
else
{
@@ -969,7 +1025,7 @@ namespace Discord
if (channel != null)
{
var msg = channel.RemoveMessage(data.Id);
await MessageDeleted.RaiseAsync(msg).ConfigureAwait(false);
await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create<IMessage>(msg)).ConfigureAwait(false);
}
else
{
@@ -989,7 +1045,7 @@ namespace Discord
foreach (var id in data.Ids)
{
var msg = channel.RemoveMessage(id);
await MessageDeleted.RaiseAsync(msg).ConfigureAwait(false);
await _messageDeletedEvent.InvokeAsync(msg.Id, Optional.Create<IMessage>(msg)).ConfigureAwait(false);
}
}
else
@@ -1014,7 +1070,20 @@ namespace Discord
await _gatewayLogger.WarningAsync("PRESENCE_UPDATE referenced an unknown guild.").ConfigureAwait(false);
break;
}
guild.UpdatePresence(data, DataStore);

var user = guild.GetUser(data.User.Id);
if (user != null)
{
var before = user.Clone();
user.Update(data, UpdateSource.WebSocket);
await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false);
}
else
{
user = guild.AddOrUpdateUser(data, DataStore);
user.Update(data, UpdateSource.WebSocket);
await _userUpdatedEvent.InvokeAsync(Optional.Create<IGuildUser>(), user).ConfigureAwait(false);
}
}
else
{
@@ -1034,7 +1103,7 @@ namespace Discord
{
var user = channel.GetUser(data.UserId, true);
if (user != null)
await UserIsTyping.RaiseAsync(channel, user).ConfigureAwait(false);
await _userIsTypingEvent.InvokeAsync(user, channel).ConfigureAwait(false);
}
}
break;
@@ -1057,7 +1126,11 @@ namespace Discord

var user = guild.GetUser(data.UserId);
if (user != null)
{
var before = user.Clone();
user.Update(data, UpdateSource.WebSocket);
await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false);
}
}
else
{
@@ -1076,9 +1149,9 @@ namespace Discord
var data = (payload as JToken).ToObject<API.User>(_serializer);
if (data.Id == CurrentUser.Id)
{
var before = _enablePreUpdateEvents ? CurrentUser.Clone() : null;
var before = CurrentUser.Clone();
CurrentUser.Update(data, UpdateSource.WebSocket);
await CurrentUserUpdated.RaiseAsync(before, CurrentUser).ConfigureAwait(false);
await _selfUpdatedEvent.InvokeAsync(before, CurrentUser).ConfigureAwait(false);
}
else
{


+ 0
- 2
src/Discord.Net/DiscordSocketConfig.cs View File

@@ -24,8 +24,6 @@ namespace Discord
/// This makes operations such as User.GetPermissions(Channel), User.GuildPermissions, Channel.GetUser, and Channel.Members much faster at the expense of increased memory usage.
/// </summary>
public bool UsePermissionsCache { get; set; } = false;*/
/// <summary> Gets or sets whether the a copy of a model is generated on an update event to allow you to check which properties changed. </summary>
public bool EnablePreUpdateEvents { get; set; } = true;
/// <summary>
/// Gets or sets the max number of users a guild may have for offline users to be included in the READY packet. Max is 250.
/// Decreasing this may reduce CPU usage while increasing login time and network usage.


+ 2
- 22
src/Discord.Net/Entities/WebSocket/CachedGuild.cs View File

@@ -90,11 +90,7 @@ namespace Discord
_downloaderPromise.SetResult(true);

for (int i = 0; i < model.Presences.Length; i++)
{
var presence = model.Presences[i];
UpdatePresence(presence, dataStore, members);
//AddUser(presence, dataStore, members);
}
AddOrUpdateUser(model.Presences[i], dataStore, members);
}
_members = members;
@@ -163,7 +159,7 @@ namespace Discord
}
return member;
}
public CachedGuildUser AddUser(PresenceModel model, DataStore dataStore, ConcurrentDictionary<ulong, CachedGuildUser> members = null)
public CachedGuildUser AddOrUpdateUser(PresenceModel model, DataStore dataStore, ConcurrentDictionary<ulong, CachedGuildUser> members = null)
{
members = members ?? _members;

@@ -194,22 +190,6 @@ namespace Discord
return member;
return null;
}
public void UpdatePresence(PresenceModel model, DataStore dataStore, ConcurrentDictionary<ulong, CachedGuildUser> members = null)
{
members = members ?? _members;

CachedGuildUser member;
if (members.TryGetValue(model.User.Id, out member))
member.Update(model, UpdateSource.WebSocket);
else
{
var user = Discord.GetOrAddUser(model.User, dataStore);
member = new CachedGuildUser(this, user, model);
members[user.Id] = member;
user.AddRef();
DownloadedMemberCount++;
}
}
public async Task DownloadMembersAsync()
{
if (!HasAllMembers)


+ 0
- 56
src/Discord.Net/Extensions/EventExtensions.cs View File

@@ -1,56 +0,0 @@
using System;
using System.Threading.Tasks;

namespace Discord.Extensions
{
internal static class EventExtensions
{
//TODO: Optimize these for if there is only 1 subscriber (can we do this?)
//TODO: Could we maintain our own list instead of generating one on every invocation?
public static async Task RaiseAsync(this Func<Task> eventHandler)
{
var subscriptions = eventHandler?.GetInvocationList();
if (subscriptions != null)
{
for (int i = 0; i < subscriptions.Length; i++)
await (subscriptions[i] as Func<Task>).Invoke().ConfigureAwait(false);
}
}
public static async Task RaiseAsync<T>(this Func<T, Task> eventHandler, T arg)
{
var subscriptions = eventHandler?.GetInvocationList();
if (subscriptions != null)
{
for (int i = 0; i < subscriptions.Length; i++)
await (subscriptions[i] as Func<T, Task>).Invoke(arg).ConfigureAwait(false);
}
}
public static async Task RaiseAsync<T1, T2>(this Func<T1, T2, Task> eventHandler, T1 arg1, T2 arg2)
{
var subscriptions = eventHandler?.GetInvocationList();
if (subscriptions != null)
{
for (int i = 0; i < subscriptions.Length; i++)
await (subscriptions[i] as Func<T1, T2, Task>).Invoke(arg1, arg2).ConfigureAwait(false);
}
}
public static async Task RaiseAsync<T1, T2, T3>(this Func<T1, T2, T3, Task> eventHandler, T1 arg1, T2 arg2, T3 arg3)
{
var subscriptions = eventHandler?.GetInvocationList();
if (subscriptions != null)
{
for (int i = 0; i < subscriptions.Length; i++)
await (subscriptions[i] as Func<T1, T2, T3, Task>).Invoke(arg1, arg2, arg3).ConfigureAwait(false);
}
}
public static async Task RaiseAsync<T1, T2, T3, T4>(this Func<T1, T2, T3, T4, Task> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
var subscriptions = eventHandler?.GetInvocationList();
if (subscriptions != null)
{
for (int i = 0; i < subscriptions.Length; i++)
await (subscriptions[i] as Func<T1, T2, T3, T4, Task>).Invoke(arg1, arg2, arg3, arg4).ConfigureAwait(false);
}
}
}
}

+ 9
- 9
src/Discord.Net/Logging/LogManager.cs View File

@@ -1,5 +1,4 @@
using Discord.Extensions;
using System;
using System;
using System.Threading.Tasks;

namespace Discord.Logging
@@ -8,7 +7,8 @@ namespace Discord.Logging
{
public LogSeverity Level { get; }

public event Func<LogMessage, Task> Message;
public event Func<LogMessage, Task> Message { add { _messageEvent.Add(value); } remove { _messageEvent.Remove(value); } }
private readonly AsyncEvent<Func<LogMessage, Task>> _messageEvent = new AsyncEvent<Func<LogMessage, Task>>();

public LogManager(LogSeverity minSeverity)
{
@@ -18,32 +18,32 @@ namespace Discord.Logging
public async Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false);
}
public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false);
}
public async Task LogAsync(LogSeverity severity, string source, Exception ex)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false);
}
async Task ILogger.LogAsync(LogSeverity severity, string message, Exception ex)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, "Discord", message, ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", message, ex)).ConfigureAwait(false);
}
async Task ILogger.LogAsync(LogSeverity severity, FormattableString message, Exception ex)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, "Discord", message.ToString(), ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", message.ToString(), ex)).ConfigureAwait(false);
}
async Task ILogger.LogAsync(LogSeverity severity, Exception ex)
{
if (severity <= Level)
await Message.RaiseAsync(new LogMessage(severity, "Discord", null, ex)).ConfigureAwait(false);
await _messageEvent.InvokeAsync(new LogMessage(severity, "Discord", null, ex)).ConfigureAwait(false);
}

public Task ErrorAsync(string source, string message, Exception ex = null)


+ 74
- 0
src/Discord.Net/Utilities/AsyncEvent.cs View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;

namespace Discord
{
internal class AsyncEvent<T>
{
private readonly object _subLock = new object();
internal ImmutableArray<T> _subscriptions;

public IReadOnlyList<T> Subscriptions => _subscriptions;

public AsyncEvent()
{
_subscriptions = ImmutableArray.Create<T>();
}

public void Add(T subscriber)
{
lock (_subLock)
_subscriptions = _subscriptions.Add(subscriber);
}
public void Remove(T subscriber)
{
lock (_subLock)
_subscriptions = _subscriptions.Remove(subscriber);
}
}

internal static class EventExtensions
{
public static async Task InvokeAsync(this AsyncEvent<Func<Task>> eventHandler)
{
var subscribers = eventHandler.Subscriptions;
if (subscribers.Count > 0)
{
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke().ConfigureAwait(false);
}
}
public static async Task InvokeAsync<T>(this AsyncEvent<Func<T, Task>> eventHandler, T arg)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2>(this AsyncEvent<Func<T1, T2, Task>> eventHandler, T1 arg1, T2 arg2)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3>(this AsyncEvent<Func<T1, T2, T3, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3, T4>(this AsyncEvent<Func<T1, T2, T3, T4, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3, arg4).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3, T4, T5>(this AsyncEvent<Func<T1, T2, T3, T4, T5, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3, arg4, arg5).ConfigureAwait(false);
}
}
}

Loading…
Cancel
Save