diff --git a/src/Discord.Net.Shared/TaskExtensions.cs b/src/Discord.Net.Shared/TaskExtensions.cs index 9d58ce9ea..520114f02 100644 --- a/src/Discord.Net.Shared/TaskExtensions.cs +++ b/src/Discord.Net.Shared/TaskExtensions.cs @@ -5,64 +5,64 @@ using System.Threading.Tasks; namespace Discord { internal static class TaskExtensions - { - public static async Task Timeout(this Task task, int milliseconds) - { - Task timeoutTask = Task.Delay(milliseconds); - Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); - if (finishedTask == timeoutTask) - throw new TimeoutException(); - else - await task.ConfigureAwait(false); - } - public static async Task Timeout(this Task task, int milliseconds) - { - Task timeoutTask = Task.Delay(milliseconds); - Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); - if (finishedTask == timeoutTask) - throw new TimeoutException(); - else - return await task.ConfigureAwait(false); - } - public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) - { - try - { - timeoutToken.CancelAfter(milliseconds); - await task.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - if (timeoutToken.IsCancellationRequested) - throw new TimeoutException(); - throw; - } - } - public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) - { - try - { - timeoutToken.CancelAfter(milliseconds); - return await task.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - if (timeoutToken.IsCancellationRequested) - throw new TimeoutException(); - throw; - } - } + { + public static async Task Timeout(this Task task, int milliseconds) + { + Task timeoutTask = Task.Delay(milliseconds); + Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); + if (finishedTask == timeoutTask) + throw new TimeoutException(); + else + await task.ConfigureAwait(false); + } + public static async Task Timeout(this Task task, int milliseconds) + { + Task timeoutTask = Task.Delay(milliseconds); + Task finishedTask = await Task.WhenAny(task, timeoutTask).ConfigureAwait(false); + if (finishedTask == timeoutTask) + throw new TimeoutException(); + else + return await task.ConfigureAwait(false); + } + public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) + { + try + { + timeoutToken.CancelAfter(milliseconds); + await task.ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (timeoutToken.IsCancellationRequested) + throw new TimeoutException(); + throw; + } + } + public static async Task Timeout(this Task task, int milliseconds, CancellationTokenSource timeoutToken) + { + try + { + timeoutToken.CancelAfter(milliseconds); + return await task.ConfigureAwait(false); + } + catch (OperationCanceledException) + { + if (timeoutToken.IsCancellationRequested) + throw new TimeoutException(); + throw; + } + } - public static async Task Wait(this CancellationTokenSource tokenSource) - { - var token = tokenSource.Token; - try { await Task.Delay(-1, token).ConfigureAwait(false); } - catch (OperationCanceledException) { } //Expected - } - public static async Task Wait(this CancellationToken token) - { - try { await Task.Delay(-1, token).ConfigureAwait(false); } - catch (OperationCanceledException) { } //Expected - } - } + public static async Task Wait(this CancellationTokenSource tokenSource) + { + var token = tokenSource.Token; + try { await Task.Delay(-1, token).ConfigureAwait(false); } + catch (OperationCanceledException) { } //Expected + } + public static async Task Wait(this CancellationToken token) + { + try { await Task.Delay(-1, token).ConfigureAwait(false); } + catch (OperationCanceledException) { } //Expected + } + } } diff --git a/src/Discord.Net.Shared/TaskHelper.cs b/src/Discord.Net.Shared/TaskHelper.cs index 22a60f2b2..4d0fb0ae5 100644 --- a/src/Discord.Net.Shared/TaskHelper.cs +++ b/src/Discord.Net.Shared/TaskHelper.cs @@ -2,12 +2,12 @@ namespace Discord { - internal static class TaskHelper - { - public static Task CompletedTask { get; } - static TaskHelper() - { - CompletedTask = Task.Delay(0); - } - } + internal static class TaskHelper + { + public static Task CompletedTask { get; } + static TaskHelper() + { + CompletedTask = Task.Delay(0); + } + } } diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs index a3fcf4c5b..7c5303598 100644 --- a/src/Discord.Net/DiscordConfig.cs +++ b/src/Discord.Net/DiscordConfig.cs @@ -83,8 +83,11 @@ namespace Discord /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. public int MessageCacheSize { get { return _messageCacheSize; } set { SetValue(ref _messageCacheSize, value); } } private int _messageCacheSize = 100; - /// Maintains the LastActivity property for users, showing when they last made an action (sent message, joined server, typed, etc). - public bool TrackActivity { get { return _trackActivity; } set { SetValue(ref _trackActivity, value); } } + /// Gets or sets whether the permissions cache should be used. This makes operations such as User.GetPermissions(Channel), User.ServerPermissions and Channel.Members + public bool UsePermissionsCache { get { return _usePermissionsCache; } set { SetValue(ref _usePermissionsCache, value); } } + private bool _usePermissionsCache = true; + /// Maintains the LastActivity property for users, showing when they last made an action (sent message, joined server, typed, etc). + public bool TrackActivity { get { return _trackActivity; } set { SetValue(ref _trackActivity, value); } } private bool _trackActivity = true; } } diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 8ce0ea6c6..179e8d300 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -68,12 +68,30 @@ namespace Discord { get { - if (Type == ChannelType.Text) - return _members.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); - else if (Type == ChannelType.Voice) - return _members.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); - else - return Enumerable.Empty(); + if (IsPrivate) + return _members.Values.Select(x => x.User); + if (_client.Config.UsePermissionsCache) + { + if (Type == ChannelType.Text) + return _members.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); + else if (Type == ChannelType.Voice) + return _members.Values.Select(x => x.User).Where(x => x.VoiceChannel == this); + } + else + { + if (Type == ChannelType.Text) + { + ChannelPermissions perms = new ChannelPermissions(); + return Server.Members.Where(x => + { + UpdatePermissions(x, perms); + return perms.ReadMessages == true; + }); + } + else if (Type == ChannelType.Voice) + return Server.Members.Where(x => x.VoiceChannel == this); + } + return Enumerable.Empty(); } } [JsonProperty] @@ -115,13 +133,13 @@ namespace Discord x.Global.PrivateChannel = null; }); _permissionOverwrites = new PermissionOverwrite[0]; - _members = new ConcurrentDictionary(); + _members = new ConcurrentDictionary(); - if (recipientId != null) - { - AddMember(client.PrivateUser); - AddMember(Recipient); - } + if (recipientId != null) + { + AddMember(client.PrivateUser); + AddMember(Recipient); + } //Local Cache if (client.Config.MessageCacheSize > 0) @@ -190,40 +208,61 @@ namespace Discord internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); internal void AddMember(User user) - { - var member = new ChannelMember(user); + { + if (!_client.Config.UsePermissionsCache) + return; + + var member = new ChannelMember(user); if (_members.TryAdd(user.Id, member)) UpdatePermissions(user, member.Permissions); } internal void RemoveMember(User user) - { - ChannelMember ignored; + { + if (!_client.Config.UsePermissionsCache) + return; + + ChannelMember ignored; _members.TryRemove(user.Id, out ignored); } - internal ChannelPermissions GetPermissions(User user) - { - ChannelMember member; - if (_members.TryGetValue(user.Id, out member)) - return member.Permissions; - else - return null; + internal ChannelPermissions GetPermissions(User user) + { + if (_client.Config.UsePermissionsCache) + { + ChannelMember member; + if (_members.TryGetValue(user.Id, out member)) + return member.Permissions; + else + return null; + } + else + { + ChannelPermissions perms = new ChannelPermissions(); + UpdatePermissions(user, perms); + return perms; + } } internal void UpdatePermissions() - { - foreach (var pair in _members) - { - ChannelMember member = pair.Value; - UpdatePermissions(member.User, member.Permissions); - } + { + if (!_client.Config.UsePermissionsCache) + return; + + foreach (var pair in _members) + { + ChannelMember member = pair.Value; + UpdatePermissions(member.User, member.Permissions); + } } - internal void UpdatePermissions(User user) - { - ChannelMember member; - if (_members.TryGetValue(user.Id, out member)) - UpdatePermissions(member.User, member.Permissions); + internal void UpdatePermissions(User user) + { + if (!_client.Config.UsePermissionsCache) + return; + + ChannelMember member; + if (_members.TryGetValue(user.Id, out member)) + UpdatePermissions(member.User, member.Permissions); } - private void UpdatePermissions(User user, ChannelPermissions permissions) + internal void UpdatePermissions(User user, ChannelPermissions permissions) { uint newPermissions = 0; var server = Server; @@ -236,7 +275,7 @@ namespace Discord //Start with this user's server permissions newPermissions = server.GetPermissions(user).RawValue; - if (IsPrivate || server.Owner == user) + if (IsPrivate || user.IsOwner) newPermissions = mask; //Owners always have all permissions else { @@ -263,7 +302,8 @@ namespace Discord else newPermissions = mask; //Private messages always have all permissions - permissions.SetRawValueInternal(newPermissions); + if (newPermissions != permissions.RawValue) + permissions.SetRawValueInternal(newPermissions); } public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index ee109e2f5..81771c828 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -38,14 +38,11 @@ namespace Discord /// Returns the URL to this user's current avatar. public string IconUrl => IconId != null ? Endpoints.ServerIcon(Id, IconId) : null; - /// Returns true if the current user created this server. - public bool IsOwner => _client.CurrentUser.Id == _owner.Id; - /// Returns the user that first created this server. [JsonIgnore] public User Owner => _owner.Value; [JsonProperty] - private long? OwnerId => _owner.Id; + internal long? OwnerId => _owner.Id; private Reference _owner; /// Returns the AFK voice channel for this server (see AFKTimeout). @@ -110,6 +107,7 @@ namespace Discord internal override bool LoadReferences() { _afkChannel.Load(); + _owner.Load(); return true; } internal override void UnloadReferences() @@ -185,7 +183,7 @@ namespace Discord } var usersCache = _client.Users; - foreach (var subModel in model.Members) + foreach (var subModel in model.Members) { var user = usersCache.GetOrAdd(subModel.User.Id, Id); user.Update(subModel); @@ -279,10 +277,9 @@ namespace Discord } private void UpdatePermissions(User user, ServerPermissions permissions) { - uint oldPermissions = permissions.RawValue; uint newPermissions = 0; - if (Owner == user) + if (user.IsOwner) newPermissions = ServerPermissions.All.RawValue; else { @@ -294,7 +291,7 @@ namespace Discord if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) newPermissions = ServerPermissions.All.RawValue; - if (newPermissions != oldPermissions) + if (newPermissions != permissions.RawValue) { permissions.SetRawValueInternal(newPermissions); foreach (var channel in _channels)