diff --git a/src/Discord.Net/DiscordClient.Bans.cs b/src/Discord.Net/DiscordClient.Bans.cs
index 2fd5df60a..9d99b0909 100644
--- a/src/Discord.Net/DiscordClient.Bans.cs
+++ b/src/Discord.Net/DiscordClient.Bans.cs
@@ -36,18 +36,20 @@ namespace Discord
public Task Ban(User member)
{
if (member == null) throw new ArgumentNullException(nameof(member));
+ if (member.Server == null) throw new ArgumentException("Unable to ban a user in a private chat.");
CheckReady();
- return _api.Ban(member.ServerId, member.Id);
+ return _api.Ban(member.Server.Id, member.Id);
}
/// Unbans a user from the provided server.
- public async Task Unban(User member)
+ public async Task Unban(Server server, string userId)
{
- if (member == null) throw new ArgumentNullException(nameof(member));
+ if (server == null) throw new ArgumentNullException(nameof(server));
+ if (userId == null) throw new ArgumentNullException(nameof(userId));
CheckReady();
- try { await _api.Unban(member.ServerId, member.Id).ConfigureAwait(false); }
+ try { await _api.Unban(server.Id, userId).ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
}
}
diff --git a/src/Discord.Net/DiscordClient.Invites.cs b/src/Discord.Net/DiscordClient.Invites.cs
index 8637243f1..4e61992e9 100644
--- a/src/Discord.Net/DiscordClient.Invites.cs
+++ b/src/Discord.Net/DiscordClient.Invites.cs
@@ -10,6 +10,8 @@ namespace Discord
/// Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode
public async Task GetInvite(string inviteIdOrXkcd)
{
+ //This doesn't work well if it's an invite to a different server!
+
if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd));
CheckReady();
@@ -22,8 +24,8 @@ namespace Discord
inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1);
var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false);
- var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id);
- invite.Update(response);
+ var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id);
+ invite.Cache(); //Builds references
return invite;
}
@@ -53,8 +55,8 @@ namespace Discord
var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses,
tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false);
- var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id);
- invite.Update(response);
+ var invite = new Invite(this, response.Code, response.XkcdPass, response.Guild.Id, response.Inviter?.Id, response.Channel?.Id);
+ invite.Cache(); //Builds references
return invite;
}
diff --git a/src/Discord.Net/DiscordClient.Members.cs b/src/Discord.Net/DiscordClient.Members.cs
index 119bbec95..d30b50679 100644
--- a/src/Discord.Net/DiscordClient.Members.cs
+++ b/src/Discord.Net/DiscordClient.Members.cs
@@ -121,7 +121,7 @@ namespace Discord
if (member == null) throw new ArgumentNullException(nameof(member));
CheckReady();
- return _api.EditMember(member.ServerId, member.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id));
+ return _api.EditMember(member.Server?.Id, member.Id, mute: mute, deaf: deaf, roles: roles.Select(x => x.Id));
}
}
}
\ No newline at end of file
diff --git a/src/Discord.Net/DiscordClient.Messages.cs b/src/Discord.Net/DiscordClient.Messages.cs
index 9796e9318..7b7311cdb 100644
--- a/src/Discord.Net/DiscordClient.Messages.cs
+++ b/src/Discord.Net/DiscordClient.Messages.cs
@@ -25,7 +25,7 @@ namespace Discord
else
{
var msg = new Message(_client, id, channelId, userId);
- msg.Cache(); //Creates references to channel/server
+ msg.Cache(); //Builds references
return msg;
}
}
diff --git a/src/Discord.Net/DiscordClient.Permissions.cs b/src/Discord.Net/DiscordClient.Permissions.cs
index 291ef1881..23e963a13 100644
--- a/src/Discord.Net/DiscordClient.Permissions.cs
+++ b/src/Discord.Net/DiscordClient.Permissions.cs
@@ -81,9 +81,17 @@ namespace Discord
if (changed)
{
if (targetType == PermissionTarget.Role)
- channel.InvalidatePermissionsCache();
+ {
+ var role = _roles[targetId];
+ if (role != null)
+ channel.InvalidatePermissionsCache(role);
+ }
else if (targetType == PermissionTarget.User)
- channel.InvalidatePermissionsCache(targetId);
+ {
+ var user = _users[targetId, channel.Server?.Id];
+ if (user != null)
+ channel.InvalidatePermissionsCache(user);
+ }
}
}
@@ -114,9 +122,16 @@ namespace Discord
channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).ToArray();
if (targetType == PermissionTarget.Role)
- channel.InvalidatePermissionsCache();
+ {
+ var role = _roles[userOrRoleId];
+ channel.InvalidatePermissionsCache(role);
+ }
else if (targetType == PermissionTarget.User)
- channel.InvalidatePermissionsCache(userOrRoleId);
+ {
+ var user = _users[userOrRoleId, channel.Server?.Id];
+ if (user != null)
+ channel.InvalidatePermissionsCache(user);
+ }
}
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
diff --git a/src/Discord.Net/DiscordClient.Roles.cs b/src/Discord.Net/DiscordClient.Roles.cs
index 10fc46ff5..f98005cde 100644
--- a/src/Discord.Net/DiscordClient.Roles.cs
+++ b/src/Discord.Net/DiscordClient.Roles.cs
@@ -7,11 +7,13 @@ namespace Discord
{
internal sealed class Roles : AsyncCollection
{
- private const string VirtualEveryoneId = "[Virtual]";
public Role VirtualEveryone { get; private set; }
public Roles(DiscordClient client, object writerLock)
- : base(client, writerLock, x => x.OnCached(), x => x.OnUncached()) { }
+ : base(client, writerLock, x => x.OnCached(), x => x.OnUncached())
+ {
+ VirtualEveryone = new Role(client, "Private", null);
+ }
public Role GetOrAdd(string id, string serverId)
=> GetOrAdd(id, () => new Role(_client, id, serverId));
diff --git a/src/Discord.Net/DiscordClient.Servers.cs b/src/Discord.Net/DiscordClient.Servers.cs
index afebc9c02..0db0ba1f3 100644
--- a/src/Discord.Net/DiscordClient.Servers.cs
+++ b/src/Discord.Net/DiscordClient.Servers.cs
@@ -57,7 +57,7 @@ namespace Discord
}
/// Returns a collection of all servers this client is a member of.
- public IEnumerable AllServers => _servers.Where(x => !x.IsVirtual);
+ public IEnumerable AllServers => _servers;
internal Servers Servers => _servers;
private readonly Servers _servers;
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
index 6e05a6259..56b6bcb18 100644
--- a/src/Discord.Net/DiscordClient.cs
+++ b/src/Discord.Net/DiscordClient.cs
@@ -59,12 +59,16 @@ namespace Discord
VoiceDisconnected += (s, e) =>
{
- foreach (var member in _users)
+ var server = _servers[e.ServerId];
+ if (server != null)
{
- if (member.ServerId == e.ServerId && member.IsSpeaking)
+ foreach (var member in server.Members)
{
- member.IsSpeaking = false;
- RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false);
+ if (member.IsSpeaking)
+ {
+ member.IsSpeaking = false;
+ RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false);
+ }
}
}
};
@@ -104,13 +108,13 @@ namespace Discord
BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
$"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.UserId}");
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
- $"Added Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}");
+ $"Added Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}");
UserRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
- $"Removed Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}");
+ $"Removed Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}");
MemberUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
- $"Updated Member: {e.Server?.Name ?? "[Private]"}/{e.UserId}");
+ $"Updated Member: {e.Server?.Name ?? "[Private]"}/{e.User.Id}");
UserVoiceStateUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
- $"Updated Member (Voice State): {e.Server?.Name ?? "[Private]"}/{e.UserId}");
+ $"Updated Member (Voice State): {e.Server?.Name ?? "[Private]"}/{e.User.Id}");
ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
"Updated Profile");
}
@@ -281,13 +285,12 @@ namespace Discord
{
try
{
- await base.OnReceivedEvent(e);
-
switch (e.Type)
{
//Global
- case "READY": //Resync
+ case "READY": //Resync
{
+ base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready
var data = e.Payload.ToObject(_serializer);
_currentUser = _users.GetOrAdd(data.User.Id, null);
_currentUser.Update(data.User);
@@ -576,10 +579,11 @@ namespace Discord
var member = _users[data.UserId, data.GuildId];
if (member != null)
{
- if (data.ChannelId != member.VoiceChannelId && member.IsSpeaking)
+ var voiceChannel = member.VoiceChannel;
+ if (voiceChannel != null && data.ChannelId != voiceChannel.Id && member.IsSpeaking)
{
member.IsSpeaking = false;
- RaiseUserIsSpeaking(member, _channels[member.VoiceChannelId], false);
+ RaiseUserIsSpeaking(member, _channels[voiceChannel.Id], false);
}
member.Update(data);
RaiseUserVoiceStateUpdated(member);
@@ -611,6 +615,7 @@ namespace Discord
//Internal (handled in DiscordSimpleClient)
case "VOICE_SERVER_UPDATE":
+ await base.OnReceivedEvent(e);
break;
//Others
diff --git a/src/Discord.Net/DiscordWSClient.cs b/src/Discord.Net/DiscordWSClient.cs
index f9f6d621e..6d3549c19 100644
--- a/src/Discord.Net/DiscordWSClient.cs
+++ b/src/Discord.Net/DiscordWSClient.cs
@@ -90,7 +90,7 @@ namespace Discord
}
}
- socket.ReceivedEvent += (s, e) => OnReceivedEvent(e);
+ socket.ReceivedEvent += async (s, e) => await OnReceivedEvent(e);
return socket;
}
internal virtual VoiceWebSocket CreateVoiceSocket()
@@ -292,7 +292,7 @@ namespace Discord
}
}
- internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e)
+ internal virtual async Task OnReceivedEvent(WebSocketEventEventArgs e)
{
try
{
@@ -309,7 +309,7 @@ namespace Discord
{
string token = e.Payload.Value("token");
_voiceSocket.Host = "wss://" + e.Payload.Value("endpoint").Split(':')[0];
- return _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken);
+ await _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken);
}
}
break;
@@ -319,7 +319,6 @@ namespace Discord
{
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}");
}
- return TaskHelper.CompletedTask;
}
}
}
\ No newline at end of file
diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs
index 2a11b03db..1eda52a87 100644
--- a/src/Discord.Net/Models/Channel.cs
+++ b/src/Discord.Net/Models/Channel.cs
@@ -53,11 +53,8 @@ namespace Discord
{
get
{
- if (!_areMembersStale)
- return _members.Select(x => x.Value);
-
- _members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToDictionary(x => x.Id, x => x);
- _areMembersStale = false;
+ if (_areMembersStale)
+ UpdateMembersCache();
return _members.Select(x => x.Value);
}
}
@@ -157,22 +154,32 @@ namespace Discord
}
internal void RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message);
- internal void InvalidMembersCache()
+ internal void InvalidateMembersCache()
{
_areMembersStale = true;
}
- internal void InvalidatePermissionsCache()
+ private void UpdateMembersCache()
+ {
+ _members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).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)
{
_areMembersStale = true;
- foreach (var member in Members)
+ foreach (var member in role.Members)
member.UpdateChannelPermissions(this);
}
- internal void InvalidatePermissionsCache(string userId)
+ internal void InvalidatePermissionsCache(User user)
{
_areMembersStale = true;
- var user = _members[userId]
- if (user != null)
- user.UpdateChannelPermissions(this);
+ user.UpdateChannelPermissions(this);
}
}
}
diff --git a/src/Discord.Net/Models/GlobalUser.cs b/src/Discord.Net/Models/GlobalUser.cs
index fd44d5bb9..38dc96a05 100644
--- a/src/Discord.Net/Models/GlobalUser.cs
+++ b/src/Discord.Net/Models/GlobalUser.cs
@@ -34,7 +34,10 @@ namespace Discord
_users = new ConcurrentDictionary();
}
internal override void OnCached() { }
- internal override void OnUncached() { }
+ internal override void OnUncached()
+ {
+ //Don't need to clean _users - they're considered owned by server
+ }
internal void Update(UserInfo model)
{
@@ -44,10 +47,10 @@ namespace Discord
IsVerified = model.IsVerified;
}
- internal void AddUser(User user) => _users.TryAdd(user.Id, user);
+ internal void AddUser(User user) => _users.TryAdd(user.UniqueId, user);
internal void RemoveUser(User user)
{
- if (_users.TryRemove(user.Id, out user))
+ if (_users.TryRemove(user.UniqueId, out user))
{
if (_users.Count == 0)
_client.GlobalUsers.TryRemove(Id);
diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs
index e6b122239..0a7c1aad6 100644
--- a/src/Discord.Net/Models/Invite.cs
+++ b/src/Discord.Net/Models/Invite.cs
@@ -5,10 +5,11 @@ using Newtonsoft.Json;
namespace Discord
{
public sealed class Invite : CachedObject
- {
- private readonly string _serverId;
- private string _inviterId, _channelId;
-
+ {
+ /// Returns the unique code for this invite.
+ public string Code { get; private set; }
+ /// Returns, if enabled, an alternative human-readable code for URLs.
+ public string XkcdCode { get; }
/// Time (in seconds) until the invite expires. Set to 0 to never expire.
public int MaxAge { get; private set; }
/// The amount of times this invite has been used.
@@ -19,47 +20,72 @@ namespace Discord
public bool IsRevoked { get; private set; }
/// If true, a user accepting this invite will be kicked from the server after closing their client.
public bool IsTemporary { get; private set; }
- /// Returns, if enabled, an alternative human-readable code for URLs.
- public string XkcdPass { get; }
- /// Returns a URL for this invite using XkcdPass if available or Id if not.
- public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id);
+ /// Returns a URL for this invite using XkcdCode if available or Id if not.
+ public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Code);
/// Returns the user that created this invite.
[JsonIgnore]
- public User Inviter => _client.Users[_inviterId, _serverId];
-
+ public User Inviter { get; private set; }
+ [JsonProperty("InviterId")]
+ private readonly string _inviterId;
+
/// Returns the server this invite is to.
[JsonIgnore]
- public Server Server => _client.Servers[_serverId];
-
+ public Server Server { get; private set; }
+ [JsonProperty("ServerId")]
+ private readonly string _serverId;
+
/// Returns the channel this invite is to.
[JsonIgnore]
- public Channel Channel => _client.Channels[_channelId];
+ public Channel Channel { get; private set; }
+ [JsonProperty("ChannelId")]
+ private readonly string _channelId;
- internal Invite(DiscordClient client, string code, string xkcdPass, string serverId)
+ internal Invite(DiscordClient client, string code, string xkcdPass, string serverId, string inviterId, string channelId)
: base(client, code)
{
- XkcdPass = xkcdPass;
+ XkcdCode = xkcdPass;
_serverId = serverId;
+ _inviterId = inviterId;
+ _channelId = channelId;
}
- internal override void OnCached() { }
- internal override void OnUncached() { }
- public override string ToString() => XkcdPass ?? Id;
+ internal override void OnCached()
+ {
+ var server = _client.Servers[_serverId];
+ if (server == null)
+ server = new Server(_client, _serverId);
+ Server = server;
+ if (_inviterId != null)
+ {
+ var inviter = _client.Users[_inviterId, _serverId];
+ if (inviter == null)
+ inviter = new User(_client, _inviterId, _serverId);
+ Inviter = inviter;
+ }
- internal void Update(InviteReference model)
+ if (_channelId != null)
+ {
+ var channel = _client.Channels[_channelId];
+ if (channel == null)
+ channel = new Channel(_client, _channelId, _serverId, null);
+ Channel = channel;
+ }
+ }
+ internal override void OnUncached()
{
- if (model.Channel != null)
- _channelId = model.Channel.Id;
- if (model.Inviter != null)
- _inviterId = model.Inviter.Id;
+ Server = null;
+ Inviter = null;
+ Channel = null;
}
+ public override string ToString() => XkcdCode ?? Id;
+
+
internal void Update(InviteInfo model)
{
- Update(model as InviteReference);
if (model.IsRevoked != null)
IsRevoked = model.IsRevoked.Value;
if (model.IsTemporary != null)
diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs
index 193ab87e4..f195ef771 100644
--- a/src/Discord.Net/Models/Message.cs
+++ b/src/Discord.Net/Models/Message.cs
@@ -157,15 +157,18 @@ namespace Discord
}
internal override void OnCached()
{
+ //References
var channel = _client.Channels[_channelId];
channel.AddMessage(this);
Channel = channel;
}
internal override void OnUncached()
{
+ //References
var channel = Channel;
if (channel != null)
channel.RemoveMessage(this);
+ Channel = null;
}
internal void Update(MessageInfo model)
diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs
index 0d0ff9835..2473e8448 100644
--- a/src/Discord.Net/Models/Role.cs
+++ b/src/Discord.Net/Models/Role.cs
@@ -28,10 +28,11 @@ namespace Discord
public Server Server { get; private set; }
/// Returns true if this is the role representing all users in a server.
- public bool IsEveryone => Id == _serverId;
+ public bool IsEveryone => _serverId == null || Id == _serverId;
/// Returns a list of all members in this role.
[JsonIgnore]
public IEnumerable Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this));
+ //TODO: Add local members cache
internal Role(DiscordClient client, string id, string serverId)
: base(client, id)
@@ -41,18 +42,17 @@ namespace Discord
Permissions.Lock();
Color = new Color(0);
Color.Lock();
-
- if (IsEveryone)
- Position = int.MinValue;
}
internal override void OnCached()
{
+ //References
var server = _client.Servers[_serverId];
server.AddRole(this);
Server = server;
}
internal override void OnUncached()
{
+ //References
var server = Server;
if (server != null)
server.RemoveRole(this);
diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs
index 8cdf95925..20ea50fe8 100644
--- a/src/Discord.Net/Models/Server.cs
+++ b/src/Discord.Net/Models/Server.cs
@@ -8,21 +8,11 @@ using System.Linq;
namespace Discord
{
public sealed class Server : CachedObject
- {
- private readonly ConcurrentDictionary _bans;
- private readonly ConcurrentDictionary _channels;
- private readonly ConcurrentDictionary _members;
- private readonly ConcurrentDictionary _roles;
- private readonly ConcurrentDictionary _invites;
-
- private string _ownerId;
-
+ {
/// Returns the name of this channel.
public string Name { get; private set; }
/// Returns the current logged-in user's data for this server.
public User CurrentMember { get; internal set; }
- /// Returns true if this is a virtual server used by Discord.Net and not a real Discord server.
- public bool IsVirtual { get; internal set; }
/// Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel).
public int AFKTimeout { get; private set; }
@@ -38,49 +28,49 @@ namespace Discord
/// Returns the user that first created this server.
[JsonIgnore]
public User Owner { get; private set; }
-
- /// Returns the id of the AFK voice channel for this server (see AFKTimeout).
- public string AFKChannelId { get; private set; }
+ private string _ownerId;
+
/// Returns the AFK voice channel for this server (see AFKTimeout).
[JsonIgnore]
- public Channel AFKChannel => _client.Channels[AFKChannelId];
-
- /// Returns the id of the default channel for this server.
- public string DefaultChannelId => Id;
+ public Channel AFKChannel { get; private set; }
+
/// Returns the default channel for this server.
[JsonIgnore]
- public Channel DefaultChannel => _client.Channels[DefaultChannelId];
-
+ public Channel DefaultChannel { get; private set; }
+
/// Returns a collection of the ids of all users banned on this server.
[JsonIgnore]
public IEnumerable Bans => _bans.Select(x => x.Key);
+ private ConcurrentDictionary _bans;
/// Returns a collection of all channels within this server.
[JsonIgnore]
- public IEnumerable Channels => _channels.Select(x => _client.Channels[x.Key]);
+ public IEnumerable Channels => _channels.Select(x => x.Value);
/// Returns a collection of all channels within this server.
[JsonIgnore]
- public IEnumerable TextChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Text);
+ public IEnumerable TextChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Text);
/// Returns a collection of all channels within this server.
[JsonIgnore]
- public IEnumerable VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Voice);
-
+ public IEnumerable VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice);
+ private ConcurrentDictionary _channels;
+
/// Returns a collection of all invites to this server.
[JsonIgnore]
public IEnumerable Invites => _invites.Values;
-
+ private ConcurrentDictionary _invites;
+
/// Returns a collection of all users within this server with their server-specific data.
[JsonIgnore]
- public IEnumerable Members => _members.Select(x => _client.Users[x.Key, Id]);
+ public IEnumerable Members => _members.Select(x => x.Value);
+ private ConcurrentDictionary _members;
- /// Return the id of the role representing all users in a server.
- public string EveryoneRoleId => Id;
/// Return the the role representing all users in a server.
[JsonIgnore]
- public Role EveryoneRole => _client.Roles[EveryoneRoleId];
+ public Role EveryoneRole { get; private set; }
/// Returns a collection of all roles within this server.
[JsonIgnore]
- public IEnumerable Roles => _roles.Select(x => _client.Roles[x.Key]);
+ public IEnumerable Roles => _roles.Select(x => x.Value);
+ private ConcurrentDictionary _roles;
internal Server(DiscordClient client, string id)
: base(client, id)
@@ -98,30 +88,37 @@ namespace Discord
internal override void OnUncached()
{
//Global Cache
- var channels = _client.Channels;
- foreach (var channel in _channels)
- channels.TryRemove(channel.Key);
-
- var members = _client.Users;
- foreach (var user in _members)
- members.TryRemove(user.Key, Id);
-
- var roles = _client.Roles;
- foreach (var role in _roles)
- roles.TryRemove(role.Key);
+ var globalChannels = _client.Channels;
+ var channels = _channels;
+ foreach (var channel in channels)
+ globalChannels.TryRemove(channel.Key);
+ channels.Clear();
+
+ var globalMembers = _client.Users;
+ var members = _members;
+ foreach (var user in members)
+ globalMembers.TryRemove(user.Key, Id);
+ members.Clear();
+
+ var globalRoles = _client.Roles;
+ var roles = _roles;
+ foreach (var role in roles)
+ globalRoles.TryRemove(role.Key);
+ roles.Clear();
//Local Cache
- foreach (var invite in _invites)
+ var invites = _invites;
+ foreach (var invite in invites)
invite.Value.Uncache();
- _invites.Clear();
+ invites.Clear();
_bans.Clear();
- }
+ }
internal void Update(GuildInfo model)
{
//Can be null
- AFKChannelId = model.AFKChannelId;
+ AFKChannel = _client.Channels[model.AFKChannelId];
if (model.AFKTimeout != null)
AFKTimeout = model.AFKTimeout.Value;
@@ -139,18 +136,18 @@ namespace Discord
if (model.Roles != null)
{
- var roles = _client.Roles;
- foreach (var subModel in model.Roles)
+ var roleCache = _client.Roles;
+ foreach (var x in model.Roles)
{
- var role = roles.GetOrAdd(subModel.Id, Id);
- role.Update(subModel);
- }
- }
+ var role = roleCache.GetOrAdd(x.Id, Id);
+ role.Update(x);
+ }
+ }
}
internal void Update(ExtendedGuildInfo model)
{
Update(model as GuildInfo);
-
+
var channels = _client.Channels;
foreach (var subModel in model.Channels)
{
@@ -158,22 +155,22 @@ namespace Discord
channel.Update(subModel);
}
- var users = _client.GlobalUsers;
- var members = _client.Users;
+ var usersCache = _client.GlobalUsers;
+ var membersCache = _client.Users;
foreach (var subModel in model.Members)
{
- var member = members.GetOrAdd(subModel.User.Id, Id);
+ var member = membersCache.GetOrAdd(subModel.User.Id, Id);
member.Update(subModel);
}
foreach (var subModel in model.VoiceStates)
{
- var member = members[subModel.UserId, Id];
+ var member = membersCache[subModel.UserId, Id];
if (member != null)
member.Update(subModel);
}
foreach (var subModel in model.Presences)
{
- var member = members[subModel.User.Id, Id];
+ var member = membersCache[subModel.User.Id, Id];
if (member != null)
member.Update(subModel);
}
@@ -193,9 +190,13 @@ namespace Discord
internal void AddChannel(Channel channel)
{
- _channels.TryAdd(channel.Id, channel);
- foreach (var member in Members)
- member.AddChannel(channel);
+ if (_channels.TryAdd(channel.Id, channel))
+ {
+ if (channel.Id == Id)
+ DefaultChannel = channel;
+ foreach (var member in Members)
+ member.AddChannel(channel);
+ }
}
internal void RemoveChannel(Channel channel)
{
@@ -213,7 +214,7 @@ namespace Discord
foreach (var channel in Channels)
{
member.AddChannel(channel);
- channel.InvalidatePermissionsCache(member.Id);
+ channel.InvalidatePermissionsCache(member);
}
}
internal void RemoveMember(User member)
@@ -221,13 +222,27 @@ namespace Discord
foreach (var channel in Channels)
{
member.RemoveChannel(channel);
- channel.InvalidatePermissionsCache(member.Id);
+ channel.InvalidatePermissionsCache(member);
}
_members.TryRemove(member.Id, out member);
}
internal void HasMember(User user) => _members.ContainsKey(user.Id);
- internal void AddRole(Role role) => _roles.TryAdd(role.Id, role);
- internal void RemoveRole(Role role) => _roles.TryRemove(role.Id, out role);
+ internal void AddRole(Role role)
+ {
+ if (_roles.TryAdd(role.Id, role))
+ {
+ if (role.Id == Id)
+ EveryoneRole = role;
+ }
+ }
+ internal void RemoveRole(Role role)
+ {
+ if (_roles.TryRemove(role.Id, out role))
+ {
+ if (role.Id == Id)
+ EveryoneRole = null;
+ }
+ }
}
}
diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs
index 72af1165b..2e4021ac7 100644
--- a/src/Discord.Net/Models/User.cs
+++ b/src/Discord.Net/Models/User.cs
@@ -9,17 +9,14 @@ namespace Discord
{
public class User : CachedObject
{
- private static readonly string[] _initialRoleIds = new string[0];
-
internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId;
private ConcurrentDictionary _channels;
private ConcurrentDictionary _permissions;
private ServerPermissions _serverPermissions;
- private string[] _roleIds;
/// Returns a unique identifier combining this user's id with its server's.
- internal string UniqueId => GetId(Id, ServerId);
+ internal string UniqueId => GetId(Id, _serverId);
/// Returns the name of this user on this server.
public string Name { get; private set; }
/// Returns a by-name unique identifier separating this user from others with the same name.
@@ -52,48 +49,55 @@ namespace Discord
private DateTime _lastOnline;
[JsonIgnore]
- internal GlobalUser GlobalUser => _client.GlobalUsers[Id];
+ internal GlobalUser GlobalUser { get; private set; }
- public string ServerId { get; }
[JsonIgnore]
- public Server Server => _client.Servers[ServerId];
+ public Server Server { get; private set; }
+ private string _serverId;
- public string VoiceChannelId { get; private set; }
[JsonIgnore]
- public Channel VoiceChannel => _client.Channels[VoiceChannelId];
+ public Channel VoiceChannel { get; private set; }
[JsonIgnore]
- public IEnumerable Roles => _roleIds.Select(x => _client.Roles[x]);
+ public IEnumerable Roles => _roles.Select(x => x.Value);
+ private Dictionary _roles;
+
/// Returns a collection of all messages this user has sent on this server that are still in cache.
[JsonIgnore]
- public IEnumerable Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == ServerId);
+ public IEnumerable Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == _serverId);
+
/// Returns a collection of all channels this user is a member of.
[JsonIgnore]
- public IEnumerable Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.Members == this);
+ public IEnumerable Channels => _channels.Select(x => x.Value);
internal User(DiscordClient client, string id, string serverId)
: base(client, id)
{
- ServerId = serverId;
+ _serverId = serverId;
Status = UserStatus.Offline;
- _roleIds = _initialRoleIds;
+ //_roles = new Dictionary();
_channels = new ConcurrentDictionary();
_permissions = new ConcurrentDictionary();
_serverPermissions = new ServerPermissions();
}
internal override void OnCached()
{
- var server = Server;
- server.AddMember(this);
- if (Id == _client.CurrentUserId)
- server.CurrentMember = this;
+ var server = _client.Servers[_serverId];
+ if (server != null)
+ {
+ server.AddMember(this);
+ if (Id == _client.CurrentUserId)
+ server.CurrentMember = this;
+ Server = server;
+ }
- var user = GlobalUser;
- if (server == null || !server.IsVirtual)
- user.AddUser(this);
+ var user = _client.GlobalUsers.GetOrAdd(Id);
+ user.AddUser(this);
+ GlobalUser = user;
}
internal override void OnUncached()
{
+ //References
var server = Server;
if (server != null)
{
@@ -101,9 +105,12 @@ namespace Discord
if (Id == _client.CurrentUserId)
server.CurrentMember = null;
}
+ Server = null;
+
var globalUser = GlobalUser;
if (globalUser != null)
globalUser.RemoveUser(this);
+ GlobalUser = null;
}
public override string ToString() => Id;
@@ -124,7 +131,7 @@ namespace Discord
if (model.JoinedAt.HasValue)
JoinedAt = model.JoinedAt.Value;
if (model.Roles != null)
- UpdateRoles(model.Roles);
+ UpdateRoles(model.Roles.Select(x => _client.Roles[x]));
UpdateServerPermissions();
}
@@ -142,7 +149,7 @@ namespace Discord
Update(model.User as UserReference);
if (model.Roles != null)
- UpdateRoles(model.Roles);
+ UpdateRoles(model.Roles.Select(x => _client.Roles[x]));
if (model.Status != null && Status != model.Status)
{
Status = UserStatus.FromString(model.Status);
@@ -165,7 +172,7 @@ namespace Discord
Token = model.Token;
if (model.ChannelId != null)
- VoiceChannelId = model.ChannelId;
+ VoiceChannel = _client.Channels[model.ChannelId];
if (model.IsSelfDeafened != null)
IsSelfDeafened = model.IsSelfDeafened.Value;
if (model.IsSelfMuted != null)
@@ -173,14 +180,16 @@ namespace Discord
if (model.IsServerSuppressed != null)
IsServerSuppressed = model.IsServerSuppressed.Value;
}
- private void UpdateRoles(string[] roleIds)
+ private void UpdateRoles(IEnumerable roles)
{
- //Set roles, with the everyone role added too
- string[] newRoles = new string[roleIds.Length + 1];
- newRoles[0] = ServerId; //Everyone
- for (int i = 0; i < roleIds.Length; i++)
- newRoles[i + 1] = roleIds[i];
- _roleIds = newRoles;
+ var newRoles = roles.ToDictionary(x => x.Id, x => x);
+ Role everyone;
+ if (_serverId != null)
+ everyone = Server.EveryoneRole;
+ else
+ everyone = _client.Roles.VirtualEveryone;
+ newRoles.Add(everyone.Id, everyone);
+ _roles = newRoles;
}
internal void UpdateActivity(DateTime? activity = null)
@@ -191,7 +200,7 @@ namespace Discord
internal void UpdateChannelPermissions(Channel channel)
{
- if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon
+ if (_roles == null) return; // We don't have all our data processed yet, this will be called again soon
var server = Server;
if (server == null || channel.Server != server) return;
@@ -225,14 +234,14 @@ namespace Discord
if (newPermissions != oldPermissions)
{
permissions.SetRawValueInternal(newPermissions);
- channel.InvalidMembersCache();
+ channel.InvalidateMembersCache();
}
permissions.SetRawValueInternal(newPermissions);
}
internal void UpdateServerPermissions()
{
- if (_roleIds == null) return; // We don't have all our data processed yet, this will be called again soon
+ if (_roles == null) return; // We don't have all our data processed yet, this will be called again soon
var server = Server;
if (server == null) return;
@@ -290,7 +299,7 @@ namespace Discord
{
if (role == null) throw new ArgumentNullException(nameof(role));
- return _roleIds.Contains(role.Id);
+ return _roles.ContainsKey(role.Id);
}
}
}
\ No newline at end of file
diff --git a/src/Discord.Net/Net/WebSockets/WebSocket.cs b/src/Discord.Net/Net/WebSockets/WebSocket.cs
index 0b72cc064..157923c3f 100644
--- a/src/Discord.Net/Net/WebSockets/WebSocket.cs
+++ b/src/Discord.Net/Net/WebSockets/WebSocket.cs
@@ -53,21 +53,21 @@ namespace Discord.Net.WebSockets
_connectedEvent = new ManualResetEventSlim(false);
_engine = new WebSocketSharpEngine(this, client.Config);
- _engine.BinaryMessage += async (s, e) =>
+ _engine.BinaryMessage += (s, e) =>
{
using (var compressed = new MemoryStream(e.Data, 2, e.Data.Length - 2))
using (var decompressed = new MemoryStream())
{
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
- await zlib.CopyToAsync(decompressed);
+ zlib.CopyTo(decompressed);
decompressed.Position = 0;
using (var reader = new StreamReader(decompressed))
- await ProcessMessage(await reader.ReadToEndAsync());
+ ProcessMessage(reader.ReadToEnd()).Wait();
}
};
- _engine.TextMessage += async (s, e) =>
+ _engine.TextMessage += (s, e) =>
{
- await ProcessMessage(e.Message);
+ /*await*/ ProcessMessage(e.Message).Wait();
};
}