| @@ -8,6 +8,19 @@ namespace Discord | |||||
| { | { | ||||
| public sealed class Channel : CachedObject<long> | public sealed class Channel : CachedObject<long> | ||||
| { | { | ||||
| private struct ChannelMember | |||||
| { | |||||
| public readonly User User; | |||||
| public readonly ChannelPermissions Permissions; | |||||
| public ChannelMember(User user) | |||||
| { | |||||
| User = user; | |||||
| Permissions = new ChannelPermissions(); | |||||
| Permissions.Lock(); | |||||
| } | |||||
| } | |||||
| public sealed class PermissionOverwrite | public sealed class PermissionOverwrite | ||||
| { | { | ||||
| public PermissionTarget TargetType { get; } | public PermissionTarget TargetType { get; } | ||||
| @@ -22,7 +35,7 @@ namespace Discord | |||||
| Permissions.Lock(); | Permissions.Lock(); | ||||
| } | } | ||||
| } | } | ||||
| /// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <summary> Returns the topic associated with this channel. </summary> | /// <summary> Returns the topic associated with this channel. </summary> | ||||
| @@ -50,13 +63,15 @@ namespace Discord | |||||
| { | { | ||||
| get | get | ||||
| { | { | ||||
| if (_areMembersStale) | |||||
| UpdateMembersCache(); | |||||
| return _members.Select(x => x.Value); | |||||
| if (Type == ChannelType.Text) | |||||
| return _members.Values.Where(x => x.Permissions.ReadMessages == true).Select(x => x.User); | |||||
| else if (Type == ChannelType.Voice) | |||||
| return Server.Members.Where(x => x.VoiceChannel == this); | |||||
| else | |||||
| return Enumerable.Empty<User>(); | |||||
| } | } | ||||
| } | } | ||||
| private Dictionary<long, User> _members; | |||||
| private bool _areMembersStale; | |||||
| private ConcurrentDictionary<long, ChannelMember> _members; | |||||
| /// <summary> Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. </summary> | /// <summary> Returns a collection of all messages the client has seen posted in this channel. This collection does not guarantee any ordering. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -89,7 +104,13 @@ namespace Discord | |||||
| x.GlobalUser.PrivateChannel = null; | x.GlobalUser.PrivateChannel = null; | ||||
| }); | }); | ||||
| _permissionOverwrites = _initialPermissionsOverwrites; | _permissionOverwrites = _initialPermissionsOverwrites; | ||||
| _areMembersStale = true; | |||||
| _members = new ConcurrentDictionary<long, ChannelMember>(); | |||||
| if (recipientId != null) | |||||
| { | |||||
| AddMember(client.PrivateUser); | |||||
| AddMember(Recipient); | |||||
| } | |||||
| //Local Cache | //Local Cache | ||||
| if (client.Config.MessageCacheLength > 0) | if (client.Config.MessageCacheLength > 0) | ||||
| @@ -135,7 +156,7 @@ namespace Discord | |||||
| _permissionOverwrites = model.PermissionOverwrites | _permissionOverwrites = model.PermissionOverwrites | ||||
| .Select(x => new PermissionOverwrite(PermissionTarget.FromString(x.Type), x.Id, x.Allow, x.Deny)) | .Select(x => new PermissionOverwrite(PermissionTarget.FromString(x.Type), x.Id, x.Allow, x.Deny)) | ||||
| .ToArray(); | .ToArray(); | ||||
| InvalidatePermissionsCache(); | |||||
| UpdatePermissions(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -156,52 +177,82 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); | internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message); | ||||
| internal void AddMember(User user) | |||||
| { | |||||
| var member = new ChannelMember(user); | |||||
| if (_members.TryAdd(user.Id, member)) | |||||
| UpdatePermissions(user, member.Permissions); | |||||
| } | |||||
| internal void RemoveMember(User user) | |||||
| { | |||||
| ChannelMember ignored; | |||||
| _members.TryRemove(user.Id, out ignored); | |||||
| } | |||||
| internal void InvalidateMembersCache() | |||||
| internal ChannelPermissions GetPermissions(User user) | |||||
| { | { | ||||
| _areMembersStale = true; | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| return member.Permissions; | |||||
| else | |||||
| return null; | |||||
| } | } | ||||
| private void UpdateMembersCache() | |||||
| internal void UpdatePermissions() | |||||
| { | { | ||||
| if (IsPrivate) | |||||
| { | |||||
| _members = new Dictionary<long, User>() | |||||
| { | |||||
| { _client.CurrentUserId, _client.PrivateUser }, | |||||
| { _recipient.Id.Value, _recipient.Value } | |||||
| }; | |||||
| } | |||||
| else if (Type == ChannelType.Text) | |||||
| foreach (var pair in _members) | |||||
| { | { | ||||
| _members = Server.Members | |||||
| .Where(x => x.GetPermissions(this)?.ReadMessages ?? false) | |||||
| .ToDictionary(x => x.Id, x => x); | |||||
| ChannelMember member = pair.Value; | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | } | ||||
| else if (Type == ChannelType.Voice) | |||||
| { | |||||
| _members = Server.Members | |||||
| .Where(x => x.VoiceChannel == this) | |||||
| .ToDictionary(x => x.Id, x => x); | |||||
| } | |||||
| _areMembersStale = false; | |||||
| } | |||||
| internal void InvalidatePermissionsCache() | |||||
| { | |||||
| UpdateMembersCache(); | |||||
| foreach (var member in _members) | |||||
| member.Value.UpdateChannelPermissions(this); | |||||
| } | } | ||||
| /*internal void InvalidatePermissionsCache(Role role) | |||||
| internal void UpdatePermissions(User user) | |||||
| { | { | ||||
| _areMembersStale = true; | |||||
| foreach (var member in role.Members) | |||||
| member.UpdateChannelPermissions(this); | |||||
| }*/ | |||||
| internal void InvalidatePermissionsCache(User user) | |||||
| ChannelMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | |||||
| private void UpdatePermissions(User user, ChannelPermissions permissions) | |||||
| { | { | ||||
| _areMembersStale = true; | |||||
| user.UpdateChannelPermissions(this); | |||||
| uint newPermissions = 0; | |||||
| var server = Server; | |||||
| //Load the mask of all permissions supported by this channel type | |||||
| var mask = ChannelPermissions.All(this).RawValue; | |||||
| if (server != null) | |||||
| { | |||||
| //Start with this user's server permissions | |||||
| newPermissions = server.GetPermissions(user).RawValue; | |||||
| if (IsPrivate || server.Owner == user) | |||||
| newPermissions = mask; //Owners always have all permissions | |||||
| else | |||||
| { | |||||
| 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 == Id && x.Permissions.Deny.RawValue != 0)) | |||||
| newPermissions &= ~denyUser.Permissions.Deny.RawValue; | |||||
| foreach (var allowUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == Id && x.Permissions.Allow.RawValue != 0)) | |||||
| newPermissions |= allowUser.Permissions.Allow.RawValue; | |||||
| if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | |||||
| newPermissions = mask; //ManageRolesOrPermissions gives all permisions | |||||
| else if (Type == ChannelType.Text && !BitHelper.GetBit(newPermissions, (int)PermissionsBits.ReadMessages)) | |||||
| newPermissions = 0; //No read permission on a text channel removes all other permissions | |||||
| else | |||||
| newPermissions &= mask; //Ensure we didnt get any permissions this channel doesnt support (from serverPerms, for example) | |||||
| } | |||||
| } | |||||
| else | |||||
| newPermissions = mask; //Private messages always have all permissions | |||||
| permissions.SetRawValueInternal(newPermissions); | |||||
| } | } | ||||
| public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; | public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id; | ||||
| @@ -67,7 +67,7 @@ namespace Discord | |||||
| Permissions.SetRawValueInternal(model.Permissions.Value); | Permissions.SetRawValueInternal(model.Permissions.Value); | ||||
| foreach (var member in Members) | foreach (var member in Members) | ||||
| member.UpdateServerPermissions(); | |||||
| Server.UpdatePermissions(member); | |||||
| } | } | ||||
| public override bool Equals(object obj) => obj is Role && (obj as Role).Id == Id; | public override bool Equals(object obj) => obj is Role && (obj as Role).Id == Id; | ||||
| @@ -8,7 +8,20 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public sealed class Server : CachedObject<long> | public sealed class Server : CachedObject<long> | ||||
| { | |||||
| { | |||||
| private struct ServerMember | |||||
| { | |||||
| public readonly User User; | |||||
| public readonly ServerPermissions Permissions; | |||||
| public ServerMember(User user) | |||||
| { | |||||
| User = user; | |||||
| Permissions = new ServerPermissions(); | |||||
| Permissions.Lock(); | |||||
| } | |||||
| } | |||||
| /// <summary> Returns the name of this channel. </summary> | /// <summary> Returns the name of this channel. </summary> | ||||
| public string Name { get; private set; } | public string Name { get; private set; } | ||||
| /// <summary> Returns the current logged-in user's data for this server. </summary> | /// <summary> Returns the current logged-in user's data for this server. </summary> | ||||
| @@ -20,8 +33,6 @@ namespace Discord | |||||
| public DateTime JoinedAt { get; private set; } | public DateTime JoinedAt { get; private set; } | ||||
| /// <summary> Returns the region for this server (see Regions). </summary> | /// <summary> Returns the region for this server (see Regions). </summary> | ||||
| public string Region { get; private set; } | public string Region { get; private set; } | ||||
| /*/// <summary> Returns the endpoint for this server's voice server. </summary> | |||||
| internal string VoiceServer { get; set; }*/ | |||||
| /// <summary> Returns true if the current user created this server. </summary> | /// <summary> Returns true if the current user created this server. </summary> | ||||
| public bool IsOwner => _client.CurrentUserId == _ownerId; | public bool IsOwner => _client.CurrentUserId == _ownerId; | ||||
| @@ -47,18 +58,18 @@ namespace Discord | |||||
| /// <summary> Returns a collection of all channels within this server. </summary> | /// <summary> Returns a collection of all channels within this server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> Channels => _channels.Select(x => x.Value); | public IEnumerable<Channel> Channels => _channels.Select(x => x.Value); | ||||
| /// <summary> Returns a collection of all channels within this server. </summary> | |||||
| /// <summary> Returns a collection of all text channels within this server. </summary> | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> TextChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Text); | public IEnumerable<Channel> TextChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Text); | ||||
| /// <summary> Returns a collection of all channels within this server. </summary> | |||||
| /// <summary> Returns a collection of all voice channels within this server. </summary> | |||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice); | ||||
| private ConcurrentDictionary<long, Channel> _channels; | private ConcurrentDictionary<long, Channel> _channels; | ||||
| /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | /// <summary> Returns a collection of all users within this server with their server-specific data. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public IEnumerable<User> Members => _members.Select(x => x.Value); | |||||
| private ConcurrentDictionary<long, User> _members; | |||||
| public IEnumerable<User> Members => _members.Select(x => x.Value.User); | |||||
| private ConcurrentDictionary<long, ServerMember> _members; | |||||
| /// <summary> Return the the role representing all users in a server. </summary> | /// <summary> Return the the role representing all users in a server. </summary> | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| @@ -75,8 +86,8 @@ namespace Discord | |||||
| //Global Cache | //Global Cache | ||||
| _channels = new ConcurrentDictionary<long, Channel>(); | _channels = new ConcurrentDictionary<long, Channel>(); | ||||
| _members = new ConcurrentDictionary<long, User>(); | |||||
| _roles = new ConcurrentDictionary<long, Role>(); | _roles = new ConcurrentDictionary<long, Role>(); | ||||
| _members = new ConcurrentDictionary<long, ServerMember>(); | |||||
| //Local Cache | //Local Cache | ||||
| _bans = new ConcurrentDictionary<long, bool>(); | _bans = new ConcurrentDictionary<long, bool>(); | ||||
| @@ -194,43 +205,34 @@ namespace Discord | |||||
| { | { | ||||
| if (channel.Id == Id) | if (channel.Id == Id) | ||||
| DefaultChannel = channel; | DefaultChannel = channel; | ||||
| foreach (var member in Members) | |||||
| member.AddChannel(channel); | |||||
| } | } | ||||
| } | } | ||||
| internal void RemoveChannel(Channel channel) | internal void RemoveChannel(Channel channel) | ||||
| { | { | ||||
| foreach (var member in Members) | |||||
| member.RemoveChannel(channel); | |||||
| _channels.TryRemove(channel.Id, out channel); | _channels.TryRemove(channel.Id, out channel); | ||||
| } | } | ||||
| internal void AddMember(User user) | internal void AddMember(User user) | ||||
| { | { | ||||
| if (_members.TryAdd(user.Id, user)) | |||||
| if (_members.TryAdd(user.Id, new ServerMember(user))) | |||||
| { | { | ||||
| if (user.Id == _ownerId) | if (user.Id == _ownerId) | ||||
| Owner = user; | Owner = user; | ||||
| foreach (var channel in TextChannels) | foreach (var channel in TextChannels) | ||||
| { | |||||
| user.AddChannel(channel); | |||||
| channel.InvalidatePermissionsCache(user); | |||||
| } | |||||
| channel.AddMember(user); | |||||
| } | } | ||||
| } | } | ||||
| internal void RemoveMember(User user) | internal void RemoveMember(User user) | ||||
| { | { | ||||
| if (_members.TryRemove(user.Id, out user)) | |||||
| ServerMember ignored; | |||||
| if (_members.TryRemove(user.Id, out ignored)) | |||||
| { | { | ||||
| if (user.Id == _ownerId) | if (user.Id == _ownerId) | ||||
| Owner = null; | Owner = null; | ||||
| foreach (var channel in Channels) | foreach (var channel in Channels) | ||||
| { | |||||
| user.RemoveChannel(channel); | |||||
| channel.InvalidatePermissionsCache(user); | |||||
| } | |||||
| channel.RemoveMember(user); | |||||
| } | } | ||||
| } | } | ||||
| internal void HasMember(User user) => _members.ContainsKey(user.Id); | internal void HasMember(User user) => _members.ContainsKey(user.Id); | ||||
| @@ -252,6 +254,45 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| internal ServerPermissions GetPermissions(User user) | |||||
| { | |||||
| ServerMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| return member.Permissions; | |||||
| else | |||||
| return null; | |||||
| } | |||||
| internal void UpdatePermissions(User user) | |||||
| { | |||||
| ServerMember member; | |||||
| if (_members.TryGetValue(user.Id, out member)) | |||||
| UpdatePermissions(member.User, member.Permissions); | |||||
| } | |||||
| private void UpdatePermissions(User user, ServerPermissions permissions) | |||||
| { | |||||
| uint oldPermissions = permissions.RawValue; | |||||
| uint newPermissions = 0; | |||||
| if (Owner == user) | |||||
| newPermissions = ServerPermissions.All.RawValue; | |||||
| else | |||||
| { | |||||
| var roles = Roles; | |||||
| foreach (var serverRole in roles) | |||||
| newPermissions |= serverRole.Permissions.RawValue; | |||||
| } | |||||
| if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | |||||
| newPermissions = ServerPermissions.All.RawValue; | |||||
| if (newPermissions != oldPermissions) | |||||
| { | |||||
| permissions.SetRawValueInternal(newPermissions); | |||||
| foreach (var channel in _channels) | |||||
| channel.Value.UpdatePermissions(user); | |||||
| } | |||||
| } | |||||
| public override bool Equals(object obj) => obj is Server && (obj as Server).Id == Id; | public override bool Equals(object obj) => obj is Server && (obj as Server).Id == Id; | ||||
| public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175); | public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175); | ||||
| public override string ToString() => Name ?? IdConvert.ToString(Id); | public override string ToString() => Name ?? IdConvert.ToString(Id); | ||||
| @@ -7,19 +7,6 @@ using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| public struct ChannelPermissionsPair | |||||
| { | |||||
| public Channel Channel; | |||||
| public ChannelPermissions Permissions; | |||||
| public ChannelPermissionsPair(Channel channel) | |||||
| { | |||||
| Channel = channel; | |||||
| Permissions = new ChannelPermissions(); | |||||
| Permissions.Lock(); | |||||
| } | |||||
| } | |||||
| public class User : CachedObject<long> | public class User : CachedObject<long> | ||||
| { | { | ||||
| internal struct CompositeKey : IEquatable<CompositeKey> | internal struct CompositeKey : IEquatable<CompositeKey> | ||||
| @@ -38,9 +25,6 @@ namespace Discord | |||||
| } | } | ||||
| internal static string GetAvatarUrl(long userId, string avatarId) => avatarId != null ? Endpoints.UserAvatar(userId, avatarId) : null; | internal static string GetAvatarUrl(long userId, string avatarId) => avatarId != null ? Endpoints.UserAvatar(userId, avatarId) : null; | ||||
| private ConcurrentDictionary<long, ChannelPermissionsPair> _permissions; | |||||
| private ServerPermissions _serverPermissions; | |||||
| /// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | /// <summary> Returns a unique identifier combining this user's id with its server's. </summary> | ||||
| internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); | internal CompositeKey UniqueId => new CompositeKey(_server.Id ?? 0, Id); | ||||
| @@ -117,10 +101,8 @@ namespace Discord | |||||
| { | { | ||||
| if (_server.Id != null) | if (_server.Id != null) | ||||
| { | { | ||||
| return _permissions | |||||
| .Where(x => x.Value.Permissions.ReadMessages) | |||||
| .Select(x => x.Value.Channel) | |||||
| .Where(x => x != null); | |||||
| return Server.Channels | |||||
| .Where(x => x.GetPermissions(this).ReadMessages); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -158,12 +140,6 @@ namespace Discord | |||||
| _roles = new Dictionary<long, Role>(); | _roles = new Dictionary<long, Role>(); | ||||
| Status = UserStatus.Offline; | Status = UserStatus.Offline; | ||||
| //_channels = new ConcurrentDictionary<string, Channel>(); | |||||
| if (serverId != null) | |||||
| { | |||||
| _permissions = new ConcurrentDictionary<long, ChannelPermissionsPair>(); | |||||
| _serverPermissions = new ServerPermissions(); | |||||
| } | |||||
| if (serverId == null) | if (serverId == null) | ||||
| UpdateRoles(null); | UpdateRoles(null); | ||||
| @@ -197,8 +173,6 @@ namespace Discord | |||||
| JoinedAt = model.JoinedAt.Value; | JoinedAt = model.JoinedAt.Value; | ||||
| if (model.Roles != null) | if (model.Roles != null) | ||||
| UpdateRoles(model.Roles.Select(x => _client.Roles[x])); | UpdateRoles(model.Roles.Select(x => _client.Roles[x])); | ||||
| UpdateServerPermissions(); | |||||
| } | } | ||||
| internal void Update(ExtendedMemberInfo model) | internal void Update(ExtendedMemberInfo model) | ||||
| { | { | ||||
| @@ -242,20 +216,9 @@ namespace Discord | |||||
| IsSelfMuted = model.IsSelfMuted.Value; | IsSelfMuted = model.IsSelfMuted.Value; | ||||
| if (model.IsServerSuppressed != null) | if (model.IsServerSuppressed != null) | ||||
| IsServerSuppressed = model.IsServerSuppressed.Value; | IsServerSuppressed = model.IsServerSuppressed.Value; | ||||
| if (_voiceChannel.Id != model.ChannelId) | |||||
| { | |||||
| var oldChannel = _voiceChannel.Value; | |||||
| if (oldChannel != null) | |||||
| oldChannel.InvalidateMembersCache(); | |||||
| _voiceChannel.Id = model.ChannelId; //Can be null | |||||
| var newChannel = _voiceChannel.Value; | |||||
| if (newChannel != null) | |||||
| newChannel.InvalidateMembersCache(); | |||||
| } | |||||
| } | |||||
| _voiceChannel.Id = model.ChannelId; //Allows null | |||||
| } | |||||
| private void UpdateRoles(IEnumerable<Role> roles) | private void UpdateRoles(IEnumerable<Role> roles) | ||||
| { | { | ||||
| Dictionary<long, Role> newRoles = new Dictionary<long, Role>(); | Dictionary<long, Role> newRoles = new Dictionary<long, Role>(); | ||||
| @@ -271,6 +234,9 @@ namespace Discord | |||||
| newRoles.Add(everyone.Id, everyone); | newRoles.Add(everyone.Id, everyone); | ||||
| } | } | ||||
| _roles = newRoles; | _roles = newRoles; | ||||
| if (!IsPrivate) | |||||
| Server.UpdatePermissions(this); | |||||
| } | } | ||||
| internal void UpdateActivity(DateTime? activity = null) | internal void UpdateActivity(DateTime? activity = null) | ||||
| @@ -278,111 +244,13 @@ namespace Discord | |||||
| if (LastActivityAt == null || activity > LastActivityAt.Value) | if (LastActivityAt == null || activity > LastActivityAt.Value) | ||||
| LastActivityAt = activity ?? DateTime.UtcNow; | LastActivityAt = activity ?? DateTime.UtcNow; | ||||
| } | } | ||||
| internal void UpdateServerPermissions() | |||||
| { | |||||
| var server = Server; | |||||
| if (server == null) return; | |||||
| uint newPermissions = 0x0; | |||||
| uint oldPermissions = _serverPermissions.RawValue; | |||||
| if (server.Owner == this) | |||||
| newPermissions = ServerPermissions.All.RawValue; | |||||
| else | |||||
| { | |||||
| var roles = Roles; | |||||
| foreach (var serverRole in roles) | |||||
| newPermissions |= serverRole.Permissions.RawValue; | |||||
| } | |||||
| if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | |||||
| newPermissions = ServerPermissions.All.RawValue; | |||||
| if (newPermissions != oldPermissions) | |||||
| { | |||||
| _serverPermissions.SetRawValueInternal(newPermissions); | |||||
| foreach (var permission in _permissions) | |||||
| UpdateChannelPermissions(permission.Value.Channel); | |||||
| } | |||||
| } | |||||
| internal void UpdateChannelPermissions(Channel channel) | |||||
| { | |||||
| var server = Server; | |||||
| if (server == null) return; | |||||
| if (channel.Server != server) throw new InvalidOperationException(); | |||||
| ChannelPermissionsPair chanPerms; | |||||
| if (!_permissions.TryGetValue(channel.Id, out chanPerms)) return; | |||||
| uint newPermissions = _serverPermissions.RawValue; | |||||
| uint oldPermissions = chanPerms.Permissions.RawValue; | |||||
| if (server.Owner == this) | |||||
| newPermissions = ChannelPermissions.All(channel).RawValue; | |||||
| else | |||||
| { | |||||
| var channelOverwrites = channel.PermissionOverwrites; | |||||
| //var roles = Roles.OrderBy(x => x.Id); | |||||
| var roles = 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 == Id && x.Permissions.Deny.RawValue != 0)) | |||||
| newPermissions &= ~denyUser.Permissions.Deny.RawValue; | |||||
| foreach (var allowUser in channelOverwrites.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == Id && x.Permissions.Allow.RawValue != 0)) | |||||
| newPermissions |= allowUser.Permissions.Allow.RawValue; | |||||
| } | |||||
| var mask = ChannelPermissions.All(channel).RawValue; | |||||
| if (BitHelper.GetBit(newPermissions, (int)PermissionsBits.ManageRolesOrPermissions)) | |||||
| newPermissions = mask; | |||||
| else if (!BitHelper.GetBit(newPermissions, (int)PermissionsBits.ReadMessages)) | |||||
| newPermissions = ChannelPermissions.None.RawValue; | |||||
| else | |||||
| newPermissions &= mask; | |||||
| if (newPermissions != oldPermissions) | |||||
| { | |||||
| chanPerms.Permissions.SetRawValueInternal(newPermissions); | |||||
| channel.InvalidateMembersCache(); | |||||
| } | |||||
| chanPerms.Permissions.SetRawValueInternal(newPermissions); | |||||
| } | |||||
| public ServerPermissions GetServerPermissions() => _serverPermissions; | |||||
| public ServerPermissions ServerPermissions => Server.GetPermissions(this); | |||||
| public ChannelPermissions GetPermissions(Channel channel) | public ChannelPermissions GetPermissions(Channel channel) | ||||
| { | { | ||||
| if (channel == null) throw new ArgumentNullException(nameof(channel)); | if (channel == null) throw new ArgumentNullException(nameof(channel)); | ||||
| //Return static permissions if this is a private chat | |||||
| if (_server.Id == null) | |||||
| return ChannelPermissions.PrivateOnly; | |||||
| ChannelPermissionsPair chanPerms; | |||||
| if (_permissions.TryGetValue(channel.Id, out chanPerms)) | |||||
| return chanPerms.Permissions; | |||||
| return null; | |||||
| } | |||||
| internal void AddChannel(Channel channel) | |||||
| { | |||||
| if (_server.Id != null) | |||||
| { | |||||
| _permissions.TryAdd(channel.Id, new ChannelPermissionsPair(channel)); | |||||
| UpdateChannelPermissions(channel); | |||||
| } | |||||
| } | |||||
| internal void RemoveChannel(Channel channel) | |||||
| { | |||||
| if (_server.Id != null) | |||||
| { | |||||
| ChannelPermissionsPair ignored; | |||||
| //_channels.TryRemove(channel.Id, out channel); | |||||
| _permissions.TryRemove(channel.Id, out ignored); | |||||
| } | |||||
| return channel.GetPermissions(this); | |||||
| } | } | ||||
| public bool HasRole(Role role) | public bool HasRole(Role role) | ||||