diff --git a/src/Discord.Net.Net45/Discord.Net.csproj b/src/Discord.Net.Net45/Discord.Net.csproj index 72f500b19..92cb7f83b 100644 --- a/src/Discord.Net.Net45/Discord.Net.csproj +++ b/src/Discord.Net.Net45/Discord.Net.csproj @@ -409,9 +409,15 @@ Enums\LogSeverity.cs + + Enums\PermissionBits.cs + Enums\PermissionTarget.cs + + Enums\PermValue.cs + Enums\Relative.cs diff --git a/src/Discord.Net/Enums/PermValue.cs b/src/Discord.Net/Enums/PermValue.cs new file mode 100644 index 000000000..fe048b016 --- /dev/null +++ b/src/Discord.Net/Enums/PermValue.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public enum PermValue + { + Allow, + Deny, + Inherit + } +} diff --git a/src/Discord.Net/Enums/PermissionBits.cs b/src/Discord.Net/Enums/PermissionBits.cs new file mode 100644 index 000000000..0766dadc4 --- /dev/null +++ b/src/Discord.Net/Enums/PermissionBits.cs @@ -0,0 +1,31 @@ +namespace Discord +{ + internal enum PermissionBits : byte + { + //General + CreateInstantInvite = 0, + KickMembers = 1, + BanMembers = 2, + ManageRolesOrPermissions = 3, + ManageChannel = 4, + ManageServer = 5, + + //Text + ReadMessages = 10, + SendMessages = 11, + SendTTSMessages = 12, + ManageMessages = 13, + EmbedLinks = 14, + AttachFiles = 15, + ReadMessageHistory = 16, + MentionEveryone = 17, + + //Voice + Connect = 20, + Speak = 21, + MuteMembers = 22, + DeafenMembers = 23, + MoveMembers = 24, + UseVoiceActivation = 25 + } +} diff --git a/src/Discord.Net/Extensions.cs b/src/Discord.Net/Extensions.cs index f92e81881..ee4d26e9a 100644 --- a/src/Discord.Net/Extensions.cs +++ b/src/Discord.Net/Extensions.cs @@ -41,7 +41,7 @@ namespace Discord => value?.ToString(_format); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasBit(this uint value, byte bit) => ((value >> bit) & 1U) == 1; + public static bool HasBit(this uint rawValue, byte bit) => ((rawValue >> bit) & 1U) == 1; public static bool TryGetOrAdd(this ConcurrentDictionary d, TKey key, Func factory, out TValue result) diff --git a/src/Discord.Net/Legacy.cs b/src/Discord.Net/Legacy.cs index 2f9af0bff..be4cea7c0 100644 --- a/src/Discord.Net/Legacy.cs +++ b/src/Discord.Net/Legacy.cs @@ -267,13 +267,13 @@ namespace Discord.Legacy } [Obsolete("Use Server.CreateRole")] - public static Task CreateRole(this DiscordClient client, Server server, string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false) + public static Task CreateRole(this DiscordClient client, Server server, string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) { if (server == null) throw new ArgumentNullException(nameof(server)); return server.CreateRole(name, permissions, color); } [Obsolete("Use Role.Edit")] - public static Task EditRole(this DiscordClient client, Role role, string name = null, ServerPermissions permissions = null, Color color = null, bool? isHoisted = null, int? position = null) + public static Task EditRole(this DiscordClient client, Role role, string name = null, ServerPermissions? permissions = null, Color color = null, bool? isHoisted = null, int? position = null) { if (role == null) throw new ArgumentNullException(nameof(role)); return role.Edit(name, permissions, color, isHoisted, position); @@ -312,37 +312,35 @@ namespace Discord.Legacy => client.Regions; [Obsolete("Use Channel.GetPermissionRule")] - public static DualChannelPermissions GetChannelPermissions(this DiscordClient client, Channel channel, User user) + public static ChannelPermissionOverrides GetChannelPermissions(this DiscordClient client, Channel channel, User user) { if (channel == null) throw new ArgumentNullException(nameof(channel)); return channel.GetPermissionsRule(user); } [Obsolete("Use Channel.GetPermissionRule")] - public static DualChannelPermissions GetChannelPermissions(this DiscordClient client, Channel channel, Role role) + public static ChannelPermissionOverrides GetChannelPermissions(this DiscordClient client, Channel channel, Role role) { if (channel == null) throw new ArgumentNullException(nameof(channel)); return channel.GetPermissionsRule(role); } - [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, ChannelPermissions allow = null, ChannelPermissions deny = null) + [Obsolete("Use Channel.AddPermissionRule(DualChannelPermissions)", true)] + public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, ChannelPermissions allow, ChannelPermissions deny) { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.AddPermissionsRule(user, allow, deny); + throw new InvalidOperationException(); } [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, DualChannelPermissions permissions = null) + public static Task SetChannelPermissions(this DiscordClient client, Channel channel, User user, ChannelPermissionOverrides permissions) { if (channel == null) throw new ArgumentNullException(nameof(channel)); return channel.AddPermissionsRule(user, permissions); } - [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, ChannelPermissions allow = null, ChannelPermissions deny = null) + [Obsolete("Use Channel.AddPermissionRule(DualChannelPermissions)")] + public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, ChannelPermissions allow, ChannelPermissions deny) { - if (channel == null) throw new ArgumentNullException(nameof(channel)); - return channel.AddPermissionsRule(role, allow, deny); + throw new InvalidOperationException(); } [Obsolete("Use Channel.AddPermissionRule")] - public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, DualChannelPermissions permissions = null) + public static Task SetChannelPermissions(this DiscordClient client, Channel channel, Role role, ChannelPermissionOverrides permissions) { if (channel == null) throw new ArgumentNullException(nameof(channel)); return channel.AddPermissionsRule(role, permissions); diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs index 73b11441f..bab97102a 100644 --- a/src/Discord.Net/Models/Channel.cs +++ b/src/Discord.Net/Models/Channel.cs @@ -18,13 +18,13 @@ namespace Discord private struct Member { - public readonly User User; - public readonly ChannelPermissions Permissions; - public Member(User user) + public User User { get; } + public ChannelPermissions Permissions { get; } + + public Member(User user, ChannelPermissions permissions) { User = user; - Permissions = new ChannelPermissions(); - Permissions.Lock(); + Permissions = permissions; } } @@ -32,13 +32,13 @@ namespace Discord { public PermissionTarget TargetType { get; } public ulong TargetId { get; } - public DualChannelPermissions Permissions { get; } + public ChannelPermissionOverrides Permissions { get; } + internal PermissionOverwrite(PermissionTarget targetType, ulong targetId, uint allow, uint deny) { TargetType = targetType; TargetId = targetId; - Permissions = new DualChannelPermissions(allow, deny); - Permissions.Lock(); + Permissions = new ChannelPermissionOverrides(allow, deny); } } @@ -94,7 +94,7 @@ namespace Discord ChannelPermissions perms = new ChannelPermissions(); return Server.Users.Where(x => { - UpdatePermissions(x, perms); + UpdatePermissions(x, ref perms); return perms.ReadMessages == true; }); } @@ -385,8 +385,10 @@ namespace Discord foreach (var pair in _users) { - Member member = pair.Value; - UpdatePermissions(member.User, member.Permissions); + var member = pair.Value; + var perms = member.Permissions; + if (UpdatePermissions(member.User, ref perms)) + _users[pair.Key] = new Member(member.User, perms); } } internal void UpdatePermissions(User user) @@ -396,9 +398,13 @@ namespace Discord Member member; if (_users.TryGetValue(user.Id, out member)) - UpdatePermissions(member.User, member.Permissions); + { + var perms = member.Permissions; + if (UpdatePermissions(member.User, ref perms)) + _users[user.Id] = new Member(member.User, perms); + } } - internal void UpdatePermissions(User user, ChannelPermissions permissions) + internal bool UpdatePermissions(User user, ref ChannelPermissions permissions) { uint newPermissions = 0; var server = Server; @@ -418,20 +424,20 @@ namespace Discord var channelOverwrites = PermissionOverwrites; var roles = user.Roles; - foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.Deny.RawValue != 0 && roles.Any(y => y.Id == x.TargetId))) - newPermissions &= ~denyRole.Permissions.Deny.RawValue; - foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.Allow.RawValue != 0 && roles.Any(y => y.Id == x.TargetId))) - newPermissions |= allowRole.Permissions.Allow.RawValue; - foreach (var denyUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.Deny.RawValue != 0)) - newPermissions &= ~denyUser.Permissions.Deny.RawValue; - foreach (var allowUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.Allow.RawValue != 0)) - newPermissions |= allowUser.Permissions.Allow.RawValue; - - if (newPermissions.HasBit((byte)PermissionsBits.ManageRolesOrPermissions)) + foreach (var denyRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.DenyValue != 0 && roles.Any(y => y.Id == x.TargetId))) + newPermissions &= ~denyRole.Permissions.DenyValue; + foreach (var allowRole in channelOverwrites.Where(x => x.TargetType == PermissionTarget.Role && x.Permissions.AllowValue != 0 && roles.Any(y => y.Id == x.TargetId))) + newPermissions |= allowRole.Permissions.AllowValue; + foreach (var denyUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.DenyValue != 0)) + newPermissions &= ~denyUser.Permissions.DenyValue; + foreach (var allowUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id && x.Permissions.AllowValue != 0)) + newPermissions |= allowUser.Permissions.AllowValue; + + if (newPermissions.HasBit((byte)PermissionBits.ManageRolesOrPermissions)) newPermissions = mask; //ManageRolesOrPermissions gives all permisions - else if (Type == ChannelType.Text && !newPermissions.HasBit((byte)PermissionsBits.ReadMessages)) + else if (Type == ChannelType.Text && !newPermissions.HasBit((byte)PermissionBits.ReadMessages)) newPermissions = 0; //No read permission on a text channel removes all other permissions - else if (Type == ChannelType.Voice && !newPermissions.HasBit((byte)PermissionsBits.Connect)) + else if (Type == ChannelType.Voice && !newPermissions.HasBit((byte)PermissionBits.Connect)) newPermissions = 0; //No connect permissions on a voice channel removes all other permissions else newPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from serverPerms, for example) @@ -441,7 +447,11 @@ namespace Discord newPermissions = mask; //Private messages always have all permissions if (newPermissions != permissions.RawValue) - permissions.SetRawValueInternal(newPermissions); + { + permissions = new ChannelPermissions(newPermissions); + return true; + } + return false; } internal ChannelPermissions GetPermissions(User user) { @@ -451,17 +461,17 @@ namespace Discord if (_users.TryGetValue(user.Id, out member)) return member.Permissions; else - return null; + return ChannelPermissions.None; } else { ChannelPermissions perms = new ChannelPermissions(); - UpdatePermissions(user, perms); + UpdatePermissions(user, ref perms); return perms; } } - public DualChannelPermissions GetPermissionsRule(User user) + public ChannelPermissionOverrides GetPermissionsRule(User user) { if (user == null) throw new ArgumentNullException(nameof(user)); @@ -470,7 +480,7 @@ namespace Discord .Select(x => x.Permissions) .FirstOrDefault(); } - public DualChannelPermissions GetPermissionsRule(Role role) + public ChannelPermissionOverrides GetPermissionsRule(Role role) { if (role == null) throw new ArgumentNullException(nameof(role)); @@ -480,38 +490,38 @@ namespace Discord .FirstOrDefault(); } - public Task AddPermissionsRule(User user, ChannelPermissions allow = null, ChannelPermissions deny = null) + public Task AddPermissionsRule(User user, ChannelPermissions allow, ChannelPermissions deny) { if (user == null) throw new ArgumentNullException(nameof(user)); - return AddPermissionsRule(user.Id, PermissionTarget.User, allow, deny); + return AddPermissionsRule(user.Id, PermissionTarget.User, allow.RawValue, deny.RawValue); } - public Task AddPermissionsRule(User user, DualChannelPermissions permissions = null) + public Task AddPermissionsRule(User user, ChannelPermissionOverrides permissions) { if (user == null) throw new ArgumentNullException(nameof(user)); - return AddPermissionsRule(user.Id, PermissionTarget.User, permissions?.Allow, permissions?.Deny); + return AddPermissionsRule(user.Id, PermissionTarget.User, permissions.AllowValue, permissions.DenyValue); } - public Task AddPermissionsRule(Role role, ChannelPermissions allow = null, ChannelPermissions deny = null) + public Task AddPermissionsRule(Role role, ChannelPermissions allow, ChannelPermissions deny) { if (role == null) throw new ArgumentNullException(nameof(role)); - return AddPermissionsRule(role.Id, PermissionTarget.Role, allow, deny); + return AddPermissionsRule(role.Id, PermissionTarget.Role, allow.RawValue, deny.RawValue); } - public Task AddPermissionsRule(Role role, DualChannelPermissions permissions = null) + public Task AddPermissionsRule(Role role, ChannelPermissionOverrides permissions) { if (role == null) throw new ArgumentNullException(nameof(role)); - return AddPermissionsRule(role.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny); + return AddPermissionsRule(role.Id, PermissionTarget.Role, permissions.AllowValue, permissions.DenyValue); } - private Task AddPermissionsRule(ulong targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null) + private Task AddPermissionsRule(ulong targetId, PermissionTarget targetType, uint allow, uint deny) { var request = new AddChannelPermissionsRequest(Id) { TargetId = targetId, TargetType = targetType.Value, - Allow = allow?.RawValue ?? 0, - Deny = deny?.RawValue ?? 0 + Allow = allow, + Deny = deny }; return Client.ClientAPI.Send(request); } @@ -543,9 +553,10 @@ namespace Discord if (!Client.Config.UsePermissionsCache) return; - var member = new Member(user); - if (_users.TryAdd(user.Id, member)) - UpdatePermissions(user, member.Permissions); + var perms = new ChannelPermissions(); + UpdatePermissions(user, ref perms); + var member = new Member(user, ChannelPermissions.None); + _users[user.Id] = new Member(user, ChannelPermissions.None); } internal void RemoveUser(ulong id) { @@ -565,7 +576,7 @@ namespace Discord if (user != null) { ChannelPermissions perms = new ChannelPermissions(); - UpdatePermissions(user, perms); + UpdatePermissions(user, ref perms); if (perms.ReadMessages) return user; } diff --git a/src/Discord.Net/Models/Color.cs b/src/Discord.Net/Models/Color.cs index 70c624e04..bdadebfd6 100644 --- a/src/Discord.Net/Models/Color.cs +++ b/src/Discord.Net/Models/Color.cs @@ -2,97 +2,47 @@ namespace Discord { - public class Color : IEquatable + public class Color { - private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); - - public static readonly Color Default = PresetColor(0); - - public static readonly Color Teal = PresetColor(0x1ABC9C); - public static readonly Color DarkTeal = PresetColor(0x11806A); - public static readonly Color Green = PresetColor(0x2ECC71); - public static readonly Color DarkGreen = PresetColor(0x1F8B4C); - public static readonly Color Blue = PresetColor(0x3498DB); - public static readonly Color DarkBlue = PresetColor(0x206694); - public static readonly Color Purple = PresetColor(0x9B59B6); - public static readonly Color DarkPurple = PresetColor(0x71368A); - public static readonly Color Magenta = PresetColor(0xE91E63); - public static readonly Color DarkMagenta = PresetColor(0xAD1457); - public static readonly Color Gold = PresetColor(0xF1C40F); - public static readonly Color DarkGold = PresetColor(0xC27C0E); - public static readonly Color Orange = PresetColor(0xE67E22); - public static readonly Color DarkOrange = PresetColor(0xA84300); - public static readonly Color Red = PresetColor(0xE74C3C); - public static readonly Color DarkRed = PresetColor(0x992D22); - - public static readonly Color LighterGrey = PresetColor(0x95A5A6); - public static readonly Color DarkGrey = PresetColor(0x607D8B); - public static readonly Color LightGrey = PresetColor(0x979C9F); - public static readonly Color DarkerGrey = PresetColor(0x546E7A); - - private static Color PresetColor(uint packedValue) - { - Color color = new Color(packedValue); - color.Lock(); - return color; - } - - private bool _isLocked; - private uint _rawValue; - public uint RawValue - { - get { return _rawValue; } - set - { - if (_isLocked) - throw new InvalidOperationException("Unable to edit cached colors directly, use Copy() to make an editable copy."); - _rawValue = value; - } - } + public static readonly Color Default = new Color(0); + + public static readonly Color Teal = new Color(0x1ABC9C); + public static readonly Color DarkTeal = new Color(0x11806A); + public static readonly Color Green = new Color(0x2ECC71); + public static readonly Color DarkGreen = new Color(0x1F8B4C); + public static readonly Color Blue = new Color(0x3498DB); + public static readonly Color DarkBlue = new Color(0x206694); + public static readonly Color Purple = new Color(0x9B59B6); + public static readonly Color DarkPurple = new Color(0x71368A); + public static readonly Color Magenta = new Color(0xE91E63); + public static readonly Color DarkMagenta = new Color(0xAD1457); + public static readonly Color Gold = new Color(0xF1C40F); + public static readonly Color DarkGold = new Color(0xC27C0E); + public static readonly Color Orange = new Color(0xE67E22); + public static readonly Color DarkOrange = new Color(0xA84300); + public static readonly Color Red = new Color(0xE74C3C); + public static readonly Color DarkRed = new Color(0x992D22); + + public static readonly Color LighterGrey = new Color(0x95A5A6); + public static readonly Color DarkGrey = new Color(0x607D8B); + public static readonly Color LightGrey = new Color(0x979C9F); + public static readonly Color DarkerGrey = new Color(0x546E7A); + + public uint RawValue { get; } - public Color(uint rawValue) { _rawValue = rawValue; } + public Color(uint rawValue) { RawValue = rawValue; } public Color(byte r, byte g, byte b) : this(((uint)r << 16) | ((uint)g << 8) | b) { } public Color(float r, float g, float b) : this((byte)(r * 255.0f), (byte)(g * 255.0f), (byte)(b * 255.0f)) { } /// Gets or sets the red component for this color. - public byte R { get { return GetByte(3); } set { SetByte(3, value); } } - /// Gets or sets the green component for this color. - public byte G { get { return GetByte(2); } set { SetByte(2, value); } } - /// Gets or sets the blue component for this color. - public byte B { get { return GetByte(1); } set { SetByte(1, value); } } - - internal void Lock() => _isLocked = true; - internal void SetRawValue(uint rawValue) - { - //Bypasses isLocked for API changes. - _rawValue = rawValue; - } - private byte GetByte(int pos) => (byte)(_rawValue >> (8 * (pos - 1))); - private void SetByte(int pos, byte value) - { - if (_isLocked) - throw new InvalidOperationException("Unable to edit cached colors directly, use Copy() to make an editable copy."); - - uint original = _rawValue; - int bit = 8 * (pos - 1); - uint mask = ~(0xFFU << bit); - _rawValue = (_rawValue & mask) | ((uint)value << bit); - } - - public static bool operator ==(Color a, Color b) => ((object)a == null && (object)b == null) || (a?.Equals(b) ?? false); - public static bool operator !=(Color a, Color b) => !(a == b); - public override int GetHashCode() => _rawValue.GetHashCode(); - public override bool Equals(object obj) => (obj as Color)?.Equals(this) ?? false; - public bool Equals(Color color) => color != null && color._rawValue == _rawValue; + public byte R => (byte)(RawValue >> 16); + /// Gets or sets the green component for this color. + public byte G => (byte)(RawValue >> 8); + /// Gets or sets the blue component for this color. + public byte B => (byte)(RawValue); - internal Color Clone() - { - var result = new Color(); - _cloner(this, result); - return result; - } - private Color() { } //Used for cloning + private byte GetByte(int pos) => (byte)(RawValue >> (8 * (pos - 1))); - public override string ToString() => '#' + _rawValue.ToString("X"); + public override string ToString() => '#' + RawValue.ToString("X"); } } diff --git a/src/Discord.Net/Models/Permissions.cs b/src/Discord.Net/Models/Permissions.cs index c842a8658..5fc3bc569 100644 --- a/src/Discord.Net/Models/Permissions.cs +++ b/src/Discord.Net/Models/Permissions.cs @@ -1,267 +1,335 @@ using System; +using System.Runtime.CompilerServices; namespace Discord { - internal enum PermissionsBits : byte - { - //General - CreateInstantInvite = 0, - KickMembers = 1, - BanMembers = 2, - ManageRolesOrPermissions = 3, - ManageChannel = 4, - ManageServer = 5, + public struct ServerPermissions + { + public static ServerPermissions None { get; } = new ServerPermissions(); + public static ServerPermissions All { get; } = new ServerPermissions(Convert.ToUInt32("00000011111100111111110000111111", 2)); - //Text - ReadMessages = 10, - SendMessages = 11, - SendTTSMessages = 12, - ManageMessages = 13, - EmbedLinks = 14, - AttachFiles = 15, - ReadMessageHistory = 16, - MentionEveryone = 17, + public uint RawValue { get; } - //Voice - Connect = 20, - Speak = 21, - MuteMembers = 22, - DeafenMembers = 23, - MoveMembers = 24, - UseVoiceActivation = 25 - } + /// If True, a user may create invites. + public bool CreateInstantInvite => PermissionsHelper.GetValue(RawValue, PermissionBits.CreateInstantInvite); + /// If True, a user may ban users from the server. + public bool BanMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.BanMembers); + /// If True, a user may kick users from the server. + public bool KickMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.KickMembers); + /// If True, a user may adjust roles. This also implictly grants all other permissions. + public bool ManageRoles => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageRolesOrPermissions); + /// If True, a user may create, delete and modify channels. + public bool ManageChannels => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageChannel); + /// If True, a user may adjust server properties. + public bool ManageServer => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageServer); - public class ServerPermissions : Permissions - { - private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); + /// If True, a user may join channels. + public bool ReadMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.ReadMessages); + /// If True, a user may send messages. + public bool SendMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.SendMessages); + /// If True, a user may send text-to-speech messages. + public bool SendTTSMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.SendTTSMessages); + /// If True, a user may delete messages. + public bool ManageMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageMessages); + /// If True, Discord will auto-embed links sent by this user. + public bool EmbedLinks => PermissionsHelper.GetValue(RawValue, PermissionBits.EmbedLinks); + /// If True, a user may send files. + public bool AttachFiles => PermissionsHelper.GetValue(RawValue, PermissionBits.AttachFiles); + /// If True, a user may read previous messages. + public bool ReadMessageHistory => PermissionsHelper.GetValue(RawValue, PermissionBits.ReadMessageHistory); + /// If True, a user may mention @everyone. + public bool MentionEveryone => PermissionsHelper.GetValue(RawValue, PermissionBits.MentionEveryone); - public static ServerPermissions None { get; } = new ServerPermissions(); - public static ServerPermissions All { get; } = new ServerPermissions(Convert.ToUInt32("00000011111100111111110000111111", 2)); + /// If True, a user may connect to a voice channel. + public bool Connect => PermissionsHelper.GetValue(RawValue, PermissionBits.Connect); + /// If True, a user may speak in a voice channel. + public bool Speak => PermissionsHelper.GetValue(RawValue, PermissionBits.Speak); + /// If True, a user may mute users. + public bool MuteMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.MuteMembers); + /// If True, a user may deafen users. + public bool DeafenMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.DeafenMembers); + /// If True, a user may move other users between voice channels. + public bool MoveMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.MoveMembers); + /// If True, a user may use voice activation rather than push-to-talk. + public bool UseVoiceActivation => PermissionsHelper.GetValue(RawValue, PermissionBits.UseVoiceActivation); - public ServerPermissions() : base() { } - public ServerPermissions(uint rawValue) : base(rawValue) { } - public ServerPermissions Copy() => new ServerPermissions(RawValue); - internal ServerPermissions Clone() + public ServerPermissions(bool? createInstantInvite = null, bool? manageRoles = null, + bool? kickMembers = null, bool? banMembers = null, bool? manageChannel = null, bool? manageServer = null, + bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, + bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + : this(new ServerPermissions(), createInstantInvite, manageRoles, kickMembers, banMembers, manageChannel, manageServer, readMessages, + sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, connect, speak, muteMembers, deafenMembers, + moveMembers, useVoiceActivation) { - var result = new ServerPermissions(); - _cloner(this, result); - return result; } + public ServerPermissions(ServerPermissions basePerms, bool? createInstantInvite = null, bool? manageRoles = null, + bool? kickMembers = null, bool? banMembers = null, bool? manageChannel = null, bool? manageServer = null, + bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, + bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + { + uint value = basePerms.RawValue; - /// If True, a user may ban users from the server. - public bool BanMembers { get { return GetBit(PermissionsBits.BanMembers); } set { SetBit(PermissionsBits.BanMembers, value); } } - /// If True, a user may kick users from the server. - public bool KickMembers { get { return GetBit(PermissionsBits.KickMembers); } set { SetBit(PermissionsBits.KickMembers, value); } } - /// If True, a user may adjust roles. This also implictly grants all other permissions. - public bool ManageRoles { get { return GetBit(PermissionsBits.ManageRolesOrPermissions); } set { SetBit(PermissionsBits.ManageRolesOrPermissions, value); } } - /// If True, a user may create, delete and modify channels. - public bool ManageChannels { get { return GetBit(PermissionsBits.ManageChannel); } set { SetBit(PermissionsBits.ManageChannel, value); } } - /// If True, a user may adjust server properties. - public bool ManageServer { get { return GetBit(PermissionsBits.ManageServer); } set { SetBit(PermissionsBits.ManageServer, value); } } - } + PermissionsHelper.SetValue(ref value, createInstantInvite, PermissionBits.CreateInstantInvite); + PermissionsHelper.SetValue(ref value, banMembers, PermissionBits.BanMembers); + PermissionsHelper.SetValue(ref value, kickMembers, PermissionBits.KickMembers); + PermissionsHelper.SetValue(ref value, manageRoles, PermissionBits.ManageRolesOrPermissions); + PermissionsHelper.SetValue(ref value, manageChannel, PermissionBits.ManageChannel); + PermissionsHelper.SetValue(ref value, manageServer, PermissionBits.ManageServer); + PermissionsHelper.SetValue(ref value, readMessages, PermissionBits.ReadMessages); + PermissionsHelper.SetValue(ref value, sendMessages, PermissionBits.SendMessages); + PermissionsHelper.SetValue(ref value, sendTTSMessages, PermissionBits.SendTTSMessages); + PermissionsHelper.SetValue(ref value, manageMessages, PermissionBits.ManageMessages); + PermissionsHelper.SetValue(ref value, embedLinks, PermissionBits.EmbedLinks); + PermissionsHelper.SetValue(ref value, attachFiles, PermissionBits.AttachFiles); + PermissionsHelper.SetValue(ref value, readMessageHistory, PermissionBits.ReadMessageHistory); + PermissionsHelper.SetValue(ref value, mentionEveryone, PermissionBits.MentionEveryone); + PermissionsHelper.SetValue(ref value, connect, PermissionBits.Connect); + PermissionsHelper.SetValue(ref value, speak, PermissionBits.Speak); + PermissionsHelper.SetValue(ref value, muteMembers, PermissionBits.MuteMembers); + PermissionsHelper.SetValue(ref value, deafenMembers, PermissionBits.DeafenMembers); + PermissionsHelper.SetValue(ref value, moveMembers, PermissionBits.MoveMembers); + PermissionsHelper.SetValue(ref value, useVoiceActivation, PermissionBits.UseVoiceActivation); - public class ChannelPermissions : Permissions - { - private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); + RawValue = value; + } + public ServerPermissions(uint rawValue) { RawValue = rawValue; } + } + public struct ChannelPermissions + { public static ChannelPermissions None { get; } = new ChannelPermissions(); public static ChannelPermissions TextOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000000000000111111110000011001", 2)); - public static ChannelPermissions PrivateOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000000000000011100110000000000", 2)); - public static ChannelPermissions VoiceOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000011111100000000000000011001", 2)); - public static ChannelPermissions All(Channel channel) => All(channel.Type, channel.IsPrivate); + public static ChannelPermissions PrivateOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000000000000011100110000000000", 2)); + public static ChannelPermissions VoiceOnly { get; } = new ChannelPermissions(Convert.ToUInt32("00000011111100000000000000011001", 2)); + public static ChannelPermissions All(Channel channel) => All(channel.Type, channel.IsPrivate); public static ChannelPermissions All(ChannelType channelType, bool isPrivate) - { - if (isPrivate) return PrivateOnly; - else if (channelType == ChannelType.Text) return TextOnly; - else if (channelType == ChannelType.Voice) return VoiceOnly; - else return None; - } - - public ChannelPermissions() : base() { } - public ChannelPermissions(uint rawValue) : base(rawValue) { } - public ChannelPermissions Copy() => new ChannelPermissions(RawValue); - internal ChannelPermissions Clone() { - var result = new ChannelPermissions(); - _cloner(this, result); - return result; + if (isPrivate) return PrivateOnly; + else if (channelType == ChannelType.Text) return TextOnly; + else if (channelType == ChannelType.Voice) return VoiceOnly; + else return None; } - /// If True, a user may adjust permissions. This also implictly grants all other permissions. - public bool ManagePermissions { get { return GetBit(PermissionsBits.ManageRolesOrPermissions); } set { SetBit(PermissionsBits.ManageRolesOrPermissions, value); } } - /// If True, a user may create, delete and modify this channel. - public bool ManageChannel { get { return GetBit(PermissionsBits.ManageChannel); } set { SetBit(PermissionsBits.ManageChannel, value); } } - } - - public abstract class Permissions : IEquatable - { - private bool _isLocked; - private uint _rawValue; - - protected Permissions() { } - protected Permissions(uint rawValue) { _rawValue = rawValue; } + public uint RawValue { get; } - /// If True, a user may create invites. - public bool CreateInstantInvite { get { return GetBit(PermissionsBits.CreateInstantInvite); } set { SetBit(PermissionsBits.CreateInstantInvite, value); } } - /// If True, a user may join channels. - public bool ReadMessages { get { return GetBit(PermissionsBits.ReadMessages); } set { SetBit(PermissionsBits.ReadMessages, value); } } - /// If True, a user may send messages. - public bool SendMessages { get { return GetBit(PermissionsBits.SendMessages); } set { SetBit(PermissionsBits.SendMessages, value); } } - /// If True, a user may send text-to-speech messages. - public bool SendTTSMessages { get { return GetBit(PermissionsBits.SendTTSMessages); } set { SetBit(PermissionsBits.SendTTSMessages, value); } } - /// If True, a user may delete messages. - public bool ManageMessages { get { return GetBit(PermissionsBits.ManageMessages); } set { SetBit(PermissionsBits.ManageMessages, value); } } - /// If True, Discord will auto-embed links sent by this user. - public bool EmbedLinks { get { return GetBit(PermissionsBits.EmbedLinks); } set { SetBit(PermissionsBits.EmbedLinks, value); } } - /// If True, a user may send files. - public bool AttachFiles { get { return GetBit(PermissionsBits.AttachFiles); } set { SetBit(PermissionsBits.AttachFiles, value); } } - /// If True, a user may read previous messages. - public bool ReadMessageHistory { get { return GetBit(PermissionsBits.ReadMessageHistory); } set { SetBit(PermissionsBits.ReadMessageHistory, value); } } - /// If True, a user may mention @everyone. - public bool MentionEveryone { get { return GetBit(PermissionsBits.MentionEveryone); } set { SetBit(PermissionsBits.MentionEveryone, value); } } + /// If True, a user may create invites. + public bool CreateInstantInvite => PermissionsHelper.GetValue(RawValue, PermissionBits.CreateInstantInvite); + /// If True, a user may adjust permissions. This also implictly grants all other permissions. + public bool ManagePermissions => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageRolesOrPermissions); + /// If True, a user may create, delete and modify this channel. + public bool ManageChannel => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageChannel); - /// If True, a user may connect to a voice channel. - public bool Connect { get { return GetBit(PermissionsBits.Connect); } set { SetBit(PermissionsBits.Connect, value); } } - /// If True, a user may speak in a voice channel. - public bool Speak { get { return GetBit(PermissionsBits.Speak); } set { SetBit(PermissionsBits.Speak, value); } } - /// If True, a user may mute users. - public bool MuteMembers { get { return GetBit(PermissionsBits.MuteMembers); } set { SetBit(PermissionsBits.MuteMembers, value); } } - /// If True, a user may deafen users. - public bool DeafenMembers { get { return GetBit(PermissionsBits.DeafenMembers); } set { SetBit(PermissionsBits.DeafenMembers, value); } } - /// If True, a user may move other users between voice channels. - public bool MoveMembers { get { return GetBit(PermissionsBits.MoveMembers); } set { SetBit(PermissionsBits.MoveMembers, value); } } - /// If True, a user may use voice activation rather than push-to-talk. - public bool UseVoiceActivation { get { return GetBit(PermissionsBits.UseVoiceActivation); } set { SetBit(PermissionsBits.UseVoiceActivation, value); } } + /// If True, a user may join channels. + public bool ReadMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.ReadMessages); + /// If True, a user may send messages. + public bool SendMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.SendMessages); + /// If True, a user may send text-to-speech messages. + public bool SendTTSMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.SendTTSMessages); + /// If True, a user may delete messages. + public bool ManageMessages => PermissionsHelper.GetValue(RawValue, PermissionBits.ManageMessages); + /// If True, Discord will auto-embed links sent by this user. + public bool EmbedLinks => PermissionsHelper.GetValue(RawValue, PermissionBits.EmbedLinks); + /// If True, a user may send files. + public bool AttachFiles => PermissionsHelper.GetValue(RawValue, PermissionBits.AttachFiles); + /// If True, a user may read previous messages. + public bool ReadMessageHistory => PermissionsHelper.GetValue(RawValue, PermissionBits.ReadMessageHistory); + /// If True, a user may mention @everyone. + public bool MentionEveryone => PermissionsHelper.GetValue(RawValue, PermissionBits.MentionEveryone); - public uint RawValue - { - get { return _rawValue; } - set { CheckLock(); _rawValue = value; } - } - internal void SetRawValueInternal(uint rawValue) - { - _rawValue = rawValue; - } + /// If True, a user may connect to a voice channel. + public bool Connect => PermissionsHelper.GetValue(RawValue, PermissionBits.Connect); + /// If True, a user may speak in a voice channel. + public bool Speak => PermissionsHelper.GetValue(RawValue, PermissionBits.Speak); + /// If True, a user may mute users. + public bool MuteMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.MuteMembers); + /// If True, a user may deafen users. + public bool DeafenMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.DeafenMembers); + /// If True, a user may move other users between voice channels. + public bool MoveMembers => PermissionsHelper.GetValue(RawValue, PermissionBits.MoveMembers); + /// If True, a user may use voice activation rather than push-to-talk. + public bool UseVoiceActivation => PermissionsHelper.GetValue(RawValue, PermissionBits.UseVoiceActivation); - internal bool GetBit(PermissionsBits bit) => _rawValue.HasBit((byte)bit); - internal void SetBit(PermissionsBits bit, bool value) { CheckLock(); SetBitInternal((byte)bit, value); } - internal void SetBitInternal(int pos, bool value) + public ChannelPermissions(bool? createInstantInvite = null, bool? managePermissions = null, + bool? manageChannel = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, + bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, + bool? mentionEveryone = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + : this(new ChannelPermissions(), createInstantInvite, managePermissions, manageChannel, readMessages, sendMessages, sendTTSMessages, + manageMessages, embedLinks, attachFiles, mentionEveryone, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation) { - if (value) - _rawValue |= (1U << pos); - else - _rawValue &= ~(1U << pos); } + public ChannelPermissions(ChannelPermissions basePerms, bool? createInstantInvite = null, bool? managePermissions = null, + bool? manageChannel = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, + bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, + bool? mentionEveryone = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, + bool? moveMembers = null, bool? useVoiceActivation = null) + { + uint value = basePerms.RawValue; - internal void Lock() => _isLocked = true; - protected void CheckLock() - { - if (_isLocked) - throw new InvalidOperationException("Unable to edit cached permissions directly, use Copy() to make an editable copy."); - } + PermissionsHelper.SetValue(ref value, createInstantInvite, PermissionBits.CreateInstantInvite); + PermissionsHelper.SetValue(ref value, managePermissions, PermissionBits.ManageRolesOrPermissions); + PermissionsHelper.SetValue(ref value, manageChannel, PermissionBits.ManageChannel); + PermissionsHelper.SetValue(ref value, readMessages, PermissionBits.ReadMessages); + PermissionsHelper.SetValue(ref value, sendMessages, PermissionBits.SendMessages); + PermissionsHelper.SetValue(ref value, sendTTSMessages, PermissionBits.SendTTSMessages); + PermissionsHelper.SetValue(ref value, manageMessages, PermissionBits.ManageMessages); + PermissionsHelper.SetValue(ref value, embedLinks, PermissionBits.EmbedLinks); + PermissionsHelper.SetValue(ref value, attachFiles, PermissionBits.AttachFiles); + PermissionsHelper.SetValue(ref value, readMessageHistory, PermissionBits.ReadMessageHistory); + PermissionsHelper.SetValue(ref value, mentionEveryone, PermissionBits.MentionEveryone); + PermissionsHelper.SetValue(ref value, connect, PermissionBits.Connect); + PermissionsHelper.SetValue(ref value, speak, PermissionBits.Speak); + PermissionsHelper.SetValue(ref value, muteMembers, PermissionBits.MuteMembers); + PermissionsHelper.SetValue(ref value, deafenMembers, PermissionBits.DeafenMembers); + PermissionsHelper.SetValue(ref value, moveMembers, PermissionBits.MoveMembers); + PermissionsHelper.SetValue(ref value, useVoiceActivation, PermissionBits.UseVoiceActivation); - public static bool operator ==(Permissions a, Permissions b) => ((object)a == null && (object)b == null) || (a?.Equals(b) ?? false); - public static bool operator !=(Permissions a, Permissions b) => !(a == b); - public override int GetHashCode() => _rawValue.GetHashCode(); - public override bool Equals(object obj) => (obj as Permissions)?.Equals(this) ?? false; - public bool Equals(Permissions permission) => permission?._rawValue == _rawValue; + RawValue = value; + } + public ChannelPermissions(uint rawValue) { RawValue = rawValue; } } - public class DualChannelPermissions + public struct ChannelPermissionOverrides { - private readonly static Action _cloner = DynamicIL.CreateCopyMethod(); - - public ChannelPermissions Allow { get; } - public ChannelPermissions Deny { get; } - - public DualChannelPermissions(uint allow = 0, uint deny = 0) - { - Allow = new ChannelPermissions(allow); - Deny = new ChannelPermissions(deny); - } + public uint AllowValue { get; } + public uint DenyValue { get; } /// If True, a user may create invites. - public bool? CreateInstantInvite { get { return GetBit(PermissionsBits.CreateInstantInvite); } set { SetBit(PermissionsBits.CreateInstantInvite, value); } } - /// If True, a user may join channels. - public bool? ReadMessages { get { return GetBit(PermissionsBits.ReadMessages); } set { SetBit(PermissionsBits.ReadMessages, value); } } + public PermValue CreateInstantInvite => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.CreateInstantInvite); + /// If True, a user may adjust permissions. This also implictly grants all other permissions. + public PermValue ManagePermissions => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.ManageRolesOrPermissions); + /// If True, a user may create, delete and modify this channel. + public PermValue ManageChannel => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.ManageChannel); + /// If True, a user may join channels. + public PermValue ReadMessages => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.ReadMessages); /// If True, a user may send messages. - public bool? SendMessages { get { return GetBit(PermissionsBits.SendMessages); } set { SetBit(PermissionsBits.SendMessages, value); } } + public PermValue SendMessages => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.SendMessages); /// If True, a user may send text-to-speech messages. - public bool? SendTTSMessages { get { return GetBit(PermissionsBits.SendTTSMessages); } set { SetBit(PermissionsBits.SendTTSMessages, value); } } + public PermValue SendTTSMessages => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.SendTTSMessages); /// If True, a user may delete messages. - public bool? ManageMessages { get { return GetBit(PermissionsBits.ManageMessages); } set { SetBit(PermissionsBits.ManageMessages, value); } } + public PermValue ManageMessages => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.ManageMessages); /// If True, Discord will auto-embed links sent by this user. - public bool? EmbedLinks { get { return GetBit(PermissionsBits.EmbedLinks); } set { SetBit(PermissionsBits.EmbedLinks, value); } } + public PermValue EmbedLinks => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.EmbedLinks); /// If True, a user may send files. - public bool? AttachFiles { get { return GetBit(PermissionsBits.AttachFiles); } set { SetBit(PermissionsBits.AttachFiles, value); } } + public PermValue AttachFiles => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.AttachFiles); /// If True, a user may read previous messages. - public bool? ReadMessageHistory { get { return GetBit(PermissionsBits.ReadMessageHistory); } set { SetBit(PermissionsBits.ReadMessageHistory, value); } } + public PermValue ReadMessageHistory => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.ReadMessageHistory); /// If True, a user may mention @everyone. - public bool? MentionEveryone { get { return GetBit(PermissionsBits.MentionEveryone); } set { SetBit(PermissionsBits.MentionEveryone, value); } } + public PermValue MentionEveryone => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.MentionEveryone); /// If True, a user may connect to a voice channel. - public bool? Connect { get { return GetBit(PermissionsBits.Connect); } set { SetBit(PermissionsBits.Connect, value); } } + public PermValue Connect => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.Connect); /// If True, a user may speak in a voice channel. - public bool? Speak { get { return GetBit(PermissionsBits.Speak); } set { SetBit(PermissionsBits.Speak, value); } } + public PermValue Speak => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.Speak); /// If True, a user may mute users. - public bool? MuteMembers { get { return GetBit(PermissionsBits.MuteMembers); } set { SetBit(PermissionsBits.MuteMembers, value); } } + public PermValue MuteMembers => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.MuteMembers); /// If True, a user may deafen users. - public bool? DeafenMembers { get { return GetBit(PermissionsBits.DeafenMembers); } set { SetBit(PermissionsBits.DeafenMembers, value); } } + public PermValue DeafenMembers => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.DeafenMembers); /// If True, a user may move other users between voice channels. - public bool? MoveMembers { get { return GetBit(PermissionsBits.MoveMembers); } set { SetBit(PermissionsBits.MoveMembers, value); } } - /// If True, a user may use voice activation rather than push-to-talk. - public bool? UseVoiceActivation { get { return GetBit(PermissionsBits.UseVoiceActivation); } set { SetBit(PermissionsBits.UseVoiceActivation, value); } } + public PermValue MoveMembers => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.MoveMembers); + /// If True, a user may use voice activation rather than push-to-talk. + public PermValue UseVoiceActivation => PermissionsHelper.GetValue(AllowValue, DenyValue, PermissionBits.UseVoiceActivation); + + public ChannelPermissionOverrides(PermValue? createInstantInvite = null, PermValue? managePermissions = null, + PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, + PermValue? moveMembers = null, PermValue? useVoiceActivation = null) + : this(new ChannelPermissionOverrides(), createInstantInvite, managePermissions, manageChannel, readMessages, sendMessages, sendTTSMessages, + manageMessages, embedLinks, attachFiles, mentionEveryone, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation) + { + } + public ChannelPermissionOverrides(ChannelPermissionOverrides basePerms, PermValue? createInstantInvite = null, PermValue? managePermissions = null, + PermValue? manageChannel = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, + PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, + PermValue? mentionEveryone = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, + PermValue? moveMembers = null, PermValue? useVoiceActivation = null) + { + uint allow = basePerms.AllowValue, deny = basePerms.DenyValue; - /// If True, a user may adjust permissions. This also implictly grants all other permissions. - public bool? ManagePermissions { get { return GetBit(PermissionsBits.ManageRolesOrPermissions); } set { SetBit(PermissionsBits.ManageRolesOrPermissions, value); } } - /// If True, a user may create, delete and modify this channel. - public bool? ManageChannel { get { return GetBit(PermissionsBits.ManageChannel); } set { SetBit(PermissionsBits.ManageChannel, value); } } + PermissionsHelper.SetValue(ref allow, ref deny, createInstantInvite, PermissionBits.CreateInstantInvite); + PermissionsHelper.SetValue(ref allow, ref deny, managePermissions, PermissionBits.ManageRolesOrPermissions); + PermissionsHelper.SetValue(ref allow, ref deny, manageChannel, PermissionBits.ManageChannel); + PermissionsHelper.SetValue(ref allow, ref deny, readMessages, PermissionBits.ReadMessages); + PermissionsHelper.SetValue(ref allow, ref deny, sendMessages, PermissionBits.SendMessages); + PermissionsHelper.SetValue(ref allow, ref deny, sendTTSMessages, PermissionBits.SendTTSMessages); + PermissionsHelper.SetValue(ref allow, ref deny, manageMessages, PermissionBits.ManageMessages); + PermissionsHelper.SetValue(ref allow, ref deny, embedLinks, PermissionBits.EmbedLinks); + PermissionsHelper.SetValue(ref allow, ref deny, attachFiles, PermissionBits.AttachFiles); + PermissionsHelper.SetValue(ref allow, ref deny, readMessageHistory, PermissionBits.ReadMessageHistory); + PermissionsHelper.SetValue(ref allow, ref deny, mentionEveryone, PermissionBits.MentionEveryone); + PermissionsHelper.SetValue(ref allow, ref deny, connect, PermissionBits.Connect); + PermissionsHelper.SetValue(ref allow, ref deny, speak, PermissionBits.Speak); + PermissionsHelper.SetValue(ref allow, ref deny, muteMembers, PermissionBits.MuteMembers); + PermissionsHelper.SetValue(ref allow, ref deny, deafenMembers, PermissionBits.DeafenMembers); + PermissionsHelper.SetValue(ref allow, ref deny, moveMembers, PermissionBits.MoveMembers); + PermissionsHelper.SetValue(ref allow, ref deny, useVoiceActivation, PermissionBits.UseVoiceActivation); - private bool? GetBit(PermissionsBits pos) - { - if (Allow.GetBit(pos)) - return true; - else if (Deny.GetBit(pos)) - return false; - else - return null; - } - private void SetBit(PermissionsBits pos, bool? value) - { - if (value == true) - { - Allow.SetBit(pos, true); - Deny.SetBit(pos, false); - } - else if (value == false) - { - Allow.SetBit(pos, false); - Deny.SetBit(pos, true); - } - else - { - Allow.SetBit(pos, false); - Deny.SetBit(pos, false); - } - } + AllowValue = allow; + DenyValue = deny; + } + public ChannelPermissionOverrides(uint allow = 0, uint deny = 0) + { + AllowValue = allow; + DenyValue = deny; + } + } + internal static class PermissionsHelper + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PermValue GetValue(uint allow, uint deny, PermissionBits bit) + { + if (allow.HasBit((byte)bit)) + return PermValue.Allow; + else if (deny.HasBit((byte)bit)) + return PermValue.Deny; + else + return PermValue.Inherit; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool GetValue(uint value, PermissionBits bit) => value.HasBit((byte)bit); - internal void Lock() - { - Allow.Lock(); - Deny.Lock(); - } - public DualChannelPermissions Copy() => new DualChannelPermissions(Allow.RawValue, Deny.RawValue); - internal DualChannelPermissions Clone() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref uint rawValue, bool? value, PermissionBits bit) + { + if (value.HasValue) + { + if (value == true) + SetBit(ref rawValue, bit); + else + UnsetBit(ref rawValue, bit); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetValue(ref uint allow, ref uint deny, PermValue? value, PermissionBits bit) { - var result = new DualChannelPermissions(); - _cloner(this, result); - return result; + if (value.HasValue) + { + switch (value) + { + case PermValue.Allow: + SetBit(ref allow, bit); + UnsetBit(ref deny, bit); + break; + case PermValue.Deny: + UnsetBit(ref allow, bit); + SetBit(ref deny, bit); + break; + default: + UnsetBit(ref allow, bit); + UnsetBit(ref deny, bit); + break; + } + } } - public static bool operator ==(DualChannelPermissions a, DualChannelPermissions b) => ((object)a == null && (object)b == null) || (a?.Equals(b) ?? false); - public static bool operator !=(DualChannelPermissions a, DualChannelPermissions b) => !(a == b); - public override int GetHashCode() => Allow.GetHashCode() ^ Deny.GetHashCode(); - public override bool Equals(object obj) => (obj as DualChannelPermissions)?.Equals(this) ?? false; - public bool Equals(DualChannelPermissions permission) => permission != null && permission.Allow == Allow && permission.Deny == Deny; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetBit(ref uint value, PermissionBits bit) => value |= 1U << (int)bit; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void UnsetBit(ref uint value, PermissionBits bit) => value &= ~(1U << (int)bit); } } diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs index 355f229a0..1bdec0d3c 100644 --- a/src/Discord.Net/Models/Role.cs +++ b/src/Discord.Net/Models/Role.cs @@ -19,10 +19,6 @@ namespace Discord public ulong Id { get; } /// Gets the server this role is a member of. public Server Server { get; } - /// Gets the the permissions contained by this role. - public ServerPermissions Permissions { get; } - /// Gets the color of this role. - public Color Color { get; } /// Gets the name of this role. public string Name { get; private set; } @@ -32,6 +28,10 @@ namespace Discord public int Position { get; private set; } /// Gets whether this role is managed by server (e.g. for Twitch integration) public bool IsManaged { get; private set; } + /// Gets the the permissions given to this role. + public ServerPermissions Permissions { get; private set; } + /// Gets the color of this role. + public Color Color { get; private set; } /// Gets true if this is the role representing all users in a server. public bool IsEveryone => Id == Server.Id; @@ -47,9 +47,7 @@ namespace Discord Server = server; Permissions = new ServerPermissions(0); - Permissions.Lock(); Color = new Color(0); - Color.Lock(); } internal void Update(APIRole model) @@ -63,15 +61,15 @@ namespace Discord if (model.Position != null && !IsEveryone) Position = model.Position.Value; if (model.Color != null) - Color.SetRawValue(model.Color.Value); + Color = new Color(model.Color.Value); if (model.Permissions != null) - Permissions.SetRawValueInternal(model.Permissions.Value); + Permissions = new ServerPermissions(model.Permissions.Value); foreach (var member in Members) Server.UpdatePermissions(member); } - public async Task Edit(string name = null, ServerPermissions permissions = null, Color color = null, bool? isHoisted = null, int? position = null) + public async Task Edit(string name = null, ServerPermissions? permissions = null, Color color = null, bool? isHoisted = null, int? position = null) { var updateRequest = new UpdateRoleRequest(Server.Id, Id) { diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs index 41ebe0ce8..758953781 100644 --- a/src/Discord.Net/Models/Server.cs +++ b/src/Discord.Net/Models/Server.cs @@ -39,11 +39,10 @@ namespace Discord { public readonly User User; public readonly ServerPermissions Permissions; - public Member(User user) + public Member(User user, ServerPermissions permissions) { User = user; - Permissions = new ServerPermissions(); - Permissions.Lock(); + Permissions = permissions; } } @@ -350,7 +349,7 @@ namespace Discord } /// Creates a new role. - public async Task CreateRole(string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false) + public async Task CreateRole(string name, ServerPermissions? permissions = null, Color color = null, bool isHoisted = false) { if (name == null) throw new ArgumentNullException(nameof(name)); @@ -392,17 +391,25 @@ namespace Discord if (_users.TryGetValue(user.Id, out member)) return member.Permissions; else - return null; + return ServerPermissions.None; } internal void UpdatePermissions(User user) { Member member; - if (_users.TryGetValue(user.Id, out member)) - UpdatePermissions(member.User, member.Permissions); + if (_users.TryGetValue(user.Id, out member)) + { + var perms = member.Permissions; + if (UpdatePermissions(member.User, ref perms)) + { + _users[user.Id] = new Member(member.User, perms); + foreach (var channel in _channels) + channel.Value.UpdatePermissions(user); + } + } } - private void UpdatePermissions(User user, ServerPermissions permissions) + private bool UpdatePermissions(User user, ref ServerPermissions permissions) { uint newPermissions = 0; @@ -414,22 +421,22 @@ namespace Discord newPermissions |= serverRole.Permissions.RawValue; } - if (newPermissions.HasBit((byte)PermissionsBits.ManageRolesOrPermissions)) + if (newPermissions.HasBit((byte)PermissionBits.ManageRolesOrPermissions)) newPermissions = ServerPermissions.All.RawValue; if (newPermissions != permissions.RawValue) { - permissions.SetRawValueInternal(newPermissions); - foreach (var channel in _channels) - channel.Value.UpdatePermissions(user); + permissions = new ServerPermissions(newPermissions); + return true; } + return false; } #endregion #region Users internal User AddUser(ulong id) { - Member member = new Member(new User(Client, id, this)); + Member member = new Member(new User(Client, id, this), ServerPermissions.None); if (id == Client.CurrentUser.Id) { member.User.CurrentGame = Client.CurrentGame; diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs index 60c0b072c..fa3a35e49 100644 --- a/src/Discord.Net/Models/User.cs +++ b/src/Discord.Net/Models/User.cs @@ -115,7 +115,7 @@ namespace Discord return Server.AllChannels .Where(x => { - x.UpdatePermissions(this, perms); + x.UpdatePermissions(this, ref perms); return (x.Type == ChannelType.Text && perms.ReadMessages) || (x.Type == ChannelType.Voice && perms.Connect); });