Browse Source

Lots of bug fixes

tags/docs-0.9
RogueException 9 years ago
parent
commit
1fcbd36ead
17 changed files with 269 additions and 181 deletions
  1. +6
    -4
      src/Discord.Net/DiscordClient.Bans.cs
  2. +6
    -4
      src/Discord.Net/DiscordClient.Invites.cs
  3. +1
    -1
      src/Discord.Net/DiscordClient.Members.cs
  4. +1
    -1
      src/Discord.Net/DiscordClient.Messages.cs
  5. +19
    -4
      src/Discord.Net/DiscordClient.Permissions.cs
  6. +4
    -2
      src/Discord.Net/DiscordClient.Roles.cs
  7. +1
    -1
      src/Discord.Net/DiscordClient.Servers.cs
  8. +18
    -13
      src/Discord.Net/DiscordClient.cs
  9. +3
    -4
      src/Discord.Net/DiscordWSClient.cs
  10. +19
    -12
      src/Discord.Net/Models/Channel.cs
  11. +6
    -3
      src/Discord.Net/Models/GlobalUser.cs
  12. +50
    -24
      src/Discord.Net/Models/Invite.cs
  13. +3
    -0
      src/Discord.Net/Models/Message.cs
  14. +4
    -4
      src/Discord.Net/Models/Role.cs
  15. +79
    -64
      src/Discord.Net/Models/Server.cs
  16. +44
    -35
      src/Discord.Net/Models/User.cs
  17. +5
    -5
      src/Discord.Net/Net/WebSockets/WebSocket.cs

+ 6
- 4
src/Discord.Net/DiscordClient.Bans.cs View File

@@ -36,18 +36,20 @@ namespace Discord
public Task Ban(User member) public Task Ban(User member)
{ {
if (member == null) throw new ArgumentNullException(nameof(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(); CheckReady();


return _api.Ban(member.ServerId, member.Id);
return _api.Ban(member.Server.Id, member.Id);
} }


/// <summary> Unbans a user from the provided server. </summary> /// <summary> Unbans a user from the provided server. </summary>
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(); 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) { } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
} }
} }

+ 6
- 4
src/Discord.Net/DiscordClient.Invites.cs View File

@@ -10,6 +10,8 @@ namespace Discord
/// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks> /// <remarks> Supported formats: inviteCode, xkcdCode, https://discord.gg/inviteCode, https://discord.gg/xkcdCode </remarks>
public async Task<Invite> GetInvite(string inviteIdOrXkcd) public async Task<Invite> 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)); if (inviteIdOrXkcd == null) throw new ArgumentNullException(nameof(inviteIdOrXkcd));
CheckReady(); CheckReady();


@@ -22,8 +24,8 @@ namespace Discord
inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1); inviteIdOrXkcd = inviteIdOrXkcd.Substring(index + 1);


var response = await _api.GetInvite(inviteIdOrXkcd).ConfigureAwait(false); 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; return invite;
} }


@@ -53,8 +55,8 @@ namespace Discord


var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses, var response = await _api.CreateInvite(channel.Id, maxAge: maxAge, maxUses: maxUses,
tempMembership: tempMembership, hasXkcd: hasXkcd).ConfigureAwait(false); 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; return invite;
} }




+ 1
- 1
src/Discord.Net/DiscordClient.Members.cs View File

@@ -121,7 +121,7 @@ namespace Discord
if (member == null) throw new ArgumentNullException(nameof(member)); if (member == null) throw new ArgumentNullException(nameof(member));
CheckReady(); 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));
} }
} }
} }

+ 1
- 1
src/Discord.Net/DiscordClient.Messages.cs View File

@@ -25,7 +25,7 @@ namespace Discord
else else
{ {
var msg = new Message(_client, id, channelId, userId); var msg = new Message(_client, id, channelId, userId);
msg.Cache(); //Creates references to channel/server
msg.Cache(); //Builds references
return msg; return msg;
} }
} }


+ 19
- 4
src/Discord.Net/DiscordClient.Permissions.cs View File

@@ -81,9 +81,17 @@ namespace Discord
if (changed) if (changed)
{ {
if (targetType == PermissionTarget.Role) if (targetType == PermissionTarget.Role)
channel.InvalidatePermissionsCache();
{
var role = _roles[targetId];
if (role != null)
channel.InvalidatePermissionsCache(role);
}
else if (targetType == PermissionTarget.User) 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(); channel.PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).ToArray();


if (targetType == PermissionTarget.Role) if (targetType == PermissionTarget.Role)
channel.InvalidatePermissionsCache();
{
var role = _roles[userOrRoleId];
channel.InvalidatePermissionsCache(role);
}
else if (targetType == PermissionTarget.User) 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) { } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }


+ 4
- 2
src/Discord.Net/DiscordClient.Roles.cs View File

@@ -7,11 +7,13 @@ namespace Discord
{ {
internal sealed class Roles : AsyncCollection<Role> internal sealed class Roles : AsyncCollection<Role>
{ {
private const string VirtualEveryoneId = "[Virtual]";
public Role VirtualEveryone { get; private set; } public Role VirtualEveryone { get; private set; }


public Roles(DiscordClient client, object writerLock) 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) public Role GetOrAdd(string id, string serverId)
=> GetOrAdd(id, () => new Role(_client, id, serverId)); => GetOrAdd(id, () => new Role(_client, id, serverId));


+ 1
- 1
src/Discord.Net/DiscordClient.Servers.cs View File

@@ -57,7 +57,7 @@ namespace Discord
} }


/// <summary> Returns a collection of all servers this client is a member of. </summary> /// <summary> Returns a collection of all servers this client is a member of. </summary>
public IEnumerable<Server> AllServers => _servers.Where(x => !x.IsVirtual);
public IEnumerable<Server> AllServers => _servers;
internal Servers Servers => _servers; internal Servers Servers => _servers;
private readonly Servers _servers; private readonly Servers _servers;




+ 18
- 13
src/Discord.Net/DiscordClient.cs View File

@@ -59,12 +59,16 @@ namespace Discord


VoiceDisconnected += (s, e) => 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, BanRemoved += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
$"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.UserId}"); $"Removed Ban: {e.Server?.Name ?? "[Private]"}/{e.UserId}");
UserAdded += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client, 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, 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, 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, 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, ProfileUpdated += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.Client,
"Updated Profile"); "Updated Profile");
} }
@@ -281,13 +285,12 @@ namespace Discord
{ {
try try
{ {
await base.OnReceivedEvent(e);

switch (e.Type) switch (e.Type)
{ {
//Global //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<ReadyEvent>(_serializer); var data = e.Payload.ToObject<ReadyEvent>(_serializer);
_currentUser = _users.GetOrAdd(data.User.Id, null); _currentUser = _users.GetOrAdd(data.User.Id, null);
_currentUser.Update(data.User); _currentUser.Update(data.User);
@@ -576,10 +579,11 @@ namespace Discord
var member = _users[data.UserId, data.GuildId]; var member = _users[data.UserId, data.GuildId];
if (member != null) 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; member.IsSpeaking = false;
RaiseUserIsSpeaking(member, _channels[member.VoiceChannelId], false);
RaiseUserIsSpeaking(member, _channels[voiceChannel.Id], false);
} }
member.Update(data); member.Update(data);
RaiseUserVoiceStateUpdated(member); RaiseUserVoiceStateUpdated(member);
@@ -611,6 +615,7 @@ namespace Discord


//Internal (handled in DiscordSimpleClient) //Internal (handled in DiscordSimpleClient)
case "VOICE_SERVER_UPDATE": case "VOICE_SERVER_UPDATE":
await base.OnReceivedEvent(e);
break; break;


//Others //Others


+ 3
- 4
src/Discord.Net/DiscordWSClient.cs View File

@@ -90,7 +90,7 @@ namespace Discord
} }
} }


socket.ReceivedEvent += (s, e) => OnReceivedEvent(e);
socket.ReceivedEvent += async (s, e) => await OnReceivedEvent(e);
return socket; return socket;
} }
internal virtual VoiceWebSocket CreateVoiceSocket() internal virtual VoiceWebSocket CreateVoiceSocket()
@@ -292,7 +292,7 @@ namespace Discord
} }
} }


internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e)
internal virtual async Task OnReceivedEvent(WebSocketEventEventArgs e)
{ {
try try
{ {
@@ -309,7 +309,7 @@ namespace Discord
{ {
string token = e.Payload.Value<string>("token"); string token = e.Payload.Value<string>("token");
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; _voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0];
return _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken);
await _voiceSocket.Login(_userId, _dataSocket.SessionId, token, CancelToken);
} }
} }
break; break;
@@ -319,7 +319,6 @@ namespace Discord
{ {
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}"); RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}");
} }
return TaskHelper.CompletedTask;
} }
} }
} }

+ 19
- 12
src/Discord.Net/Models/Channel.cs View File

@@ -53,11 +53,8 @@ namespace Discord
{ {
get 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); 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 RemoveMessage(Message message) => _messages.TryRemove(message.Id, out message);


internal void InvalidMembersCache()
internal void InvalidateMembersCache()
{ {
_areMembersStale = true; _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; _areMembersStale = true;
foreach (var member in Members)
foreach (var member in role.Members)
member.UpdateChannelPermissions(this); member.UpdateChannelPermissions(this);
} }
internal void InvalidatePermissionsCache(string userId)
internal void InvalidatePermissionsCache(User user)
{ {
_areMembersStale = true; _areMembersStale = true;
var user = _members[userId]
if (user != null)
user.UpdateChannelPermissions(this);
user.UpdateChannelPermissions(this);
} }
} }
} }

+ 6
- 3
src/Discord.Net/Models/GlobalUser.cs View File

@@ -34,7 +34,10 @@ namespace Discord
_users = new ConcurrentDictionary<string, User>(); _users = new ConcurrentDictionary<string, User>();
} }
internal override void OnCached() { } 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) internal void Update(UserInfo model)
{ {
@@ -44,10 +47,10 @@ namespace Discord
IsVerified = model.IsVerified; 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) internal void RemoveUser(User user)
{ {
if (_users.TryRemove(user.Id, out user))
if (_users.TryRemove(user.UniqueId, out user))
{ {
if (_users.Count == 0) if (_users.Count == 0)
_client.GlobalUsers.TryRemove(Id); _client.GlobalUsers.TryRemove(Id);


+ 50
- 24
src/Discord.Net/Models/Invite.cs View File

@@ -5,10 +5,11 @@ using Newtonsoft.Json;
namespace Discord namespace Discord
{ {
public sealed class Invite : CachedObject public sealed class Invite : CachedObject
{
private readonly string _serverId;
private string _inviterId, _channelId;
{
/// <summary> Returns the unique code for this invite. </summary>
public string Code { get; private set; }
/// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary>
public string XkcdCode { get; }
/// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary> /// <summary> Time (in seconds) until the invite expires. Set to 0 to never expire. </summary>
public int MaxAge { get; private set; } public int MaxAge { get; private set; }
/// <summary> The amount of times this invite has been used. </summary> /// <summary> The amount of times this invite has been used. </summary>
@@ -19,47 +20,72 @@ namespace Discord
public bool IsRevoked { get; private set; } public bool IsRevoked { get; private set; }
/// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary> /// <summary> If true, a user accepting this invite will be kicked from the server after closing their client. </summary>
public bool IsTemporary { get; private set; } public bool IsTemporary { get; private set; }
/// <summary> Returns, if enabled, an alternative human-readable code for URLs. </summary>
public string XkcdPass { get; }


/// <summary> Returns a URL for this invite using XkcdPass if available or Id if not. </summary>
public string Url => API.Endpoints.InviteUrl(XkcdPass ?? Id);
/// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary>
public string Url => API.Endpoints.InviteUrl(XkcdCode ?? Code);
/// <summary> Returns the user that created this invite. </summary> /// <summary> Returns the user that created this invite. </summary>
[JsonIgnore] [JsonIgnore]
public User Inviter => _client.Users[_inviterId, _serverId];
public User Inviter { get; private set; }
[JsonProperty("InviterId")]
private readonly string _inviterId;

/// <summary> Returns the server this invite is to. </summary> /// <summary> Returns the server this invite is to. </summary>
[JsonIgnore] [JsonIgnore]
public Server Server => _client.Servers[_serverId];
public Server Server { get; private set; }
[JsonProperty("ServerId")]
private readonly string _serverId;

/// <summary> Returns the channel this invite is to. </summary> /// <summary> Returns the channel this invite is to. </summary>
[JsonIgnore] [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) : base(client, code)
{ {
XkcdPass = xkcdPass;
XkcdCode = xkcdPass;
_serverId = serverId; _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) internal void Update(InviteInfo model)
{ {
Update(model as InviteReference);
if (model.IsRevoked != null) if (model.IsRevoked != null)
IsRevoked = model.IsRevoked.Value; IsRevoked = model.IsRevoked.Value;
if (model.IsTemporary != null) if (model.IsTemporary != null)


+ 3
- 0
src/Discord.Net/Models/Message.cs View File

@@ -157,15 +157,18 @@ namespace Discord
} }
internal override void OnCached() internal override void OnCached()
{ {
//References
var channel = _client.Channels[_channelId]; var channel = _client.Channels[_channelId];
channel.AddMessage(this); channel.AddMessage(this);
Channel = channel; Channel = channel;
} }
internal override void OnUncached() internal override void OnUncached()
{ {
//References
var channel = Channel; var channel = Channel;
if (channel != null) if (channel != null)
channel.RemoveMessage(this); channel.RemoveMessage(this);
Channel = null;
} }


internal void Update(MessageInfo model) internal void Update(MessageInfo model)


+ 4
- 4
src/Discord.Net/Models/Role.cs View File

@@ -28,10 +28,11 @@ namespace Discord
public Server Server { get; private set; } public Server Server { get; private set; }


/// <summary> Returns true if this is the role representing all users in a server. </summary> /// <summary> Returns true if this is the role representing all users in a server. </summary>
public bool IsEveryone => Id == _serverId;
public bool IsEveryone => _serverId == null || Id == _serverId;
/// <summary> Returns a list of all members in this role. </summary> /// <summary> Returns a list of all members in this role. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<User> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this)); public IEnumerable<User> Members => IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this));
//TODO: Add local members cache


internal Role(DiscordClient client, string id, string serverId) internal Role(DiscordClient client, string id, string serverId)
: base(client, id) : base(client, id)
@@ -41,18 +42,17 @@ namespace Discord
Permissions.Lock(); Permissions.Lock();
Color = new Color(0); Color = new Color(0);
Color.Lock(); Color.Lock();

if (IsEveryone)
Position = int.MinValue;
} }
internal override void OnCached() internal override void OnCached()
{ {
//References
var server = _client.Servers[_serverId]; var server = _client.Servers[_serverId];
server.AddRole(this); server.AddRole(this);
Server = server; Server = server;
} }
internal override void OnUncached() internal override void OnUncached()
{ {
//References
var server = Server; var server = Server;
if (server != null) if (server != null)
server.RemoveRole(this); server.RemoveRole(this);


+ 79
- 64
src/Discord.Net/Models/Server.cs View File

@@ -8,21 +8,11 @@ using System.Linq;
namespace Discord namespace Discord
{ {
public sealed class Server : CachedObject public sealed class Server : CachedObject
{
private readonly ConcurrentDictionary<string, bool> _bans;
private readonly ConcurrentDictionary<string, Channel> _channels;
private readonly ConcurrentDictionary<string, User> _members;
private readonly ConcurrentDictionary<string, Role> _roles;
private readonly ConcurrentDictionary<string, Invite> _invites;

private string _ownerId;
{
/// <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>
public User CurrentMember { get; internal set; } public User CurrentMember { get; internal set; }
/// <summary> Returns true if this is a virtual server used by Discord.Net and not a real Discord server. </summary>
public bool IsVirtual { get; internal set; }


/// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary> /// <summary> Returns the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel (see AFKChannel). </summary>
public int AFKTimeout { get; private set; } public int AFKTimeout { get; private set; }
@@ -38,49 +28,49 @@ namespace Discord
/// <summary> Returns the user that first created this server. </summary> /// <summary> Returns the user that first created this server. </summary>
[JsonIgnore] [JsonIgnore]
public User Owner { get; private set; } public User Owner { get; private set; }

/// <summary> Returns the id of the AFK voice channel for this server (see AFKTimeout). </summary>
public string AFKChannelId { get; private set; }
private string _ownerId;
/// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary> /// <summary> Returns the AFK voice channel for this server (see AFKTimeout). </summary>
[JsonIgnore] [JsonIgnore]
public Channel AFKChannel => _client.Channels[AFKChannelId];

/// <summary> Returns the id of the default channel for this server. </summary>
public string DefaultChannelId => Id;
public Channel AFKChannel { get; private set; }
/// <summary> Returns the default channel for this server. </summary> /// <summary> Returns the default channel for this server. </summary>
[JsonIgnore] [JsonIgnore]
public Channel DefaultChannel => _client.Channels[DefaultChannelId];
public Channel DefaultChannel { get; private set; }
/// <summary> Returns a collection of the ids of all users banned on this server. </summary> /// <summary> Returns a collection of the ids of all users banned on this server. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<string> Bans => _bans.Select(x => x.Key); public IEnumerable<string> Bans => _bans.Select(x => x.Key);
private ConcurrentDictionary<string, bool> _bans;
/// <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 => _client.Channels[x.Key]);
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 channels within this server. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Channel> TextChannels => _channels.Select(x => _client.Channels[x.Key]).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 channels within this server. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => _client.Channels[x.Key]).Where(x => x.Type == ChannelType.Voice);
public IEnumerable<Channel> VoiceChannels => _channels.Select(x => x.Value).Where(x => x.Type == ChannelType.Voice);
private ConcurrentDictionary<string, Channel> _channels;

/// <summary> Returns a collection of all invites to this server. </summary> /// <summary> Returns a collection of all invites to this server. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Invite> Invites => _invites.Values; public IEnumerable<Invite> Invites => _invites.Values;
private ConcurrentDictionary<string, Invite> _invites;

/// <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 => _client.Users[x.Key, Id]);
public IEnumerable<User> Members => _members.Select(x => x.Value);
private ConcurrentDictionary<string, User> _members;


/// <summary> Return the id of the role representing all users in a server. </summary>
public string EveryoneRoleId => Id;
/// <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]
public Role EveryoneRole => _client.Roles[EveryoneRoleId];
public Role EveryoneRole { get; private set; }
/// <summary> Returns a collection of all roles within this server. </summary> /// <summary> Returns a collection of all roles within this server. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Role> Roles => _roles.Select(x => _client.Roles[x.Key]);
public IEnumerable<Role> Roles => _roles.Select(x => x.Value);
private ConcurrentDictionary<string, Role> _roles;


internal Server(DiscordClient client, string id) internal Server(DiscordClient client, string id)
: base(client, id) : base(client, id)
@@ -98,30 +88,37 @@ namespace Discord
internal override void OnUncached() internal override void OnUncached()
{ {
//Global Cache //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 //Local Cache
foreach (var invite in _invites)
var invites = _invites;
foreach (var invite in invites)
invite.Value.Uncache(); invite.Value.Uncache();
_invites.Clear();
invites.Clear();


_bans.Clear(); _bans.Clear();
}
}


internal void Update(GuildInfo model) internal void Update(GuildInfo model)
{ {
//Can be null //Can be null
AFKChannelId = model.AFKChannelId;
AFKChannel = _client.Channels[model.AFKChannelId];


if (model.AFKTimeout != null) if (model.AFKTimeout != null)
AFKTimeout = model.AFKTimeout.Value; AFKTimeout = model.AFKTimeout.Value;
@@ -139,18 +136,18 @@ namespace Discord


if (model.Roles != null) 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) internal void Update(ExtendedGuildInfo model)
{ {
Update(model as GuildInfo); Update(model as GuildInfo);
var channels = _client.Channels; var channels = _client.Channels;
foreach (var subModel in model.Channels) foreach (var subModel in model.Channels)
{ {
@@ -158,22 +155,22 @@ namespace Discord
channel.Update(subModel); 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) 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); member.Update(subModel);
} }
foreach (var subModel in model.VoiceStates) foreach (var subModel in model.VoiceStates)
{ {
var member = members[subModel.UserId, Id];
var member = membersCache[subModel.UserId, Id];
if (member != null) if (member != null)
member.Update(subModel); member.Update(subModel);
} }
foreach (var subModel in model.Presences) foreach (var subModel in model.Presences)
{ {
var member = members[subModel.User.Id, Id];
var member = membersCache[subModel.User.Id, Id];
if (member != null) if (member != null)
member.Update(subModel); member.Update(subModel);
} }
@@ -193,9 +190,13 @@ namespace Discord


internal void AddChannel(Channel channel) 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) internal void RemoveChannel(Channel channel)
{ {
@@ -213,7 +214,7 @@ namespace Discord
foreach (var channel in Channels) foreach (var channel in Channels)
{ {
member.AddChannel(channel); member.AddChannel(channel);
channel.InvalidatePermissionsCache(member.Id);
channel.InvalidatePermissionsCache(member);
} }
} }
internal void RemoveMember(User member) internal void RemoveMember(User member)
@@ -221,13 +222,27 @@ namespace Discord
foreach (var channel in Channels) foreach (var channel in Channels)
{ {
member.RemoveChannel(channel); member.RemoveChannel(channel);
channel.InvalidatePermissionsCache(member.Id);
channel.InvalidatePermissionsCache(member);
} }
_members.TryRemove(member.Id, out member); _members.TryRemove(member.Id, out member);
} }
internal void HasMember(User user) => _members.ContainsKey(user.Id); 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;
}
}
} }
} }

+ 44
- 35
src/Discord.Net/Models/User.cs View File

@@ -9,17 +9,14 @@ namespace Discord
{ {
public class User : CachedObject public class User : CachedObject
{ {
private static readonly string[] _initialRoleIds = new string[0];

internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId; internal static string GetId(string userId, string serverId) => (serverId ?? "Private") + '_' + userId;


private ConcurrentDictionary<string, Channel> _channels; private ConcurrentDictionary<string, Channel> _channels;
private ConcurrentDictionary<string, ChannelPermissions> _permissions; private ConcurrentDictionary<string, ChannelPermissions> _permissions;
private ServerPermissions _serverPermissions; private ServerPermissions _serverPermissions;
private string[] _roleIds;


/// <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 string UniqueId => GetId(Id, ServerId);
internal string UniqueId => GetId(Id, _serverId);
/// <summary> Returns the name of this user on this server. </summary> /// <summary> Returns the name of this user on this server. </summary>
public string Name { get; private set; } public string Name { get; private set; }
/// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary> /// <summary> Returns a by-name unique identifier separating this user from others with the same name. </summary>
@@ -52,48 +49,55 @@ namespace Discord
private DateTime _lastOnline; private DateTime _lastOnline;


[JsonIgnore] [JsonIgnore]
internal GlobalUser GlobalUser => _client.GlobalUsers[Id];
internal GlobalUser GlobalUser { get; private set; }


public string ServerId { get; }
[JsonIgnore] [JsonIgnore]
public Server Server => _client.Servers[ServerId];
public Server Server { get; private set; }
private string _serverId;


public string VoiceChannelId { get; private set; }
[JsonIgnore] [JsonIgnore]
public Channel VoiceChannel => _client.Channels[VoiceChannelId];
public Channel VoiceChannel { get; private set; }


[JsonIgnore] [JsonIgnore]
public IEnumerable<Role> Roles => _roleIds.Select(x => _client.Roles[x]);
public IEnumerable<Role> Roles => _roles.Select(x => x.Value);
private Dictionary<string, Role> _roles;

/// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary> /// <summary> Returns a collection of all messages this user has sent on this server that are still in cache. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == ServerId);
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.UserId == Id && x.Server.Id == _serverId);

/// <summary> Returns a collection of all channels this user is a member of. </summary> /// <summary> Returns a collection of all channels this user is a member of. </summary>
[JsonIgnore] [JsonIgnore]
public IEnumerable<Channel> Channels => _client.Channels.Where(x => x.Server.Id == ServerId && x.Members == this);
public IEnumerable<Channel> Channels => _channels.Select(x => x.Value);


internal User(DiscordClient client, string id, string serverId) internal User(DiscordClient client, string id, string serverId)
: base(client, id) : base(client, id)
{ {
ServerId = serverId;
_serverId = serverId;
Status = UserStatus.Offline; Status = UserStatus.Offline;
_roleIds = _initialRoleIds;
//_roles = new Dictionary<string, Role>();
_channels = new ConcurrentDictionary<string, Channel>(); _channels = new ConcurrentDictionary<string, Channel>();
_permissions = new ConcurrentDictionary<string, ChannelPermissions>(); _permissions = new ConcurrentDictionary<string, ChannelPermissions>();
_serverPermissions = new ServerPermissions(); _serverPermissions = new ServerPermissions();
} }
internal override void OnCached() 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() internal override void OnUncached()
{ {
//References
var server = Server; var server = Server;
if (server != null) if (server != null)
{ {
@@ -101,9 +105,12 @@ namespace Discord
if (Id == _client.CurrentUserId) if (Id == _client.CurrentUserId)
server.CurrentMember = null; server.CurrentMember = null;
} }
Server = null;

var globalUser = GlobalUser; var globalUser = GlobalUser;
if (globalUser != null) if (globalUser != null)
globalUser.RemoveUser(this); globalUser.RemoveUser(this);
GlobalUser = null;
} }


public override string ToString() => Id; public override string ToString() => Id;
@@ -124,7 +131,7 @@ namespace Discord
if (model.JoinedAt.HasValue) if (model.JoinedAt.HasValue)
JoinedAt = model.JoinedAt.Value; JoinedAt = model.JoinedAt.Value;
if (model.Roles != null) if (model.Roles != null)
UpdateRoles(model.Roles);
UpdateRoles(model.Roles.Select(x => _client.Roles[x]));


UpdateServerPermissions(); UpdateServerPermissions();
} }
@@ -142,7 +149,7 @@ namespace Discord
Update(model.User as UserReference); Update(model.User as UserReference);


if (model.Roles != null) if (model.Roles != null)
UpdateRoles(model.Roles);
UpdateRoles(model.Roles.Select(x => _client.Roles[x]));
if (model.Status != null && Status != model.Status) if (model.Status != null && Status != model.Status)
{ {
Status = UserStatus.FromString(model.Status); Status = UserStatus.FromString(model.Status);
@@ -165,7 +172,7 @@ namespace Discord
Token = model.Token; Token = model.Token;


if (model.ChannelId != null) if (model.ChannelId != null)
VoiceChannelId = model.ChannelId;
VoiceChannel = _client.Channels[model.ChannelId];
if (model.IsSelfDeafened != null) if (model.IsSelfDeafened != null)
IsSelfDeafened = model.IsSelfDeafened.Value; IsSelfDeafened = model.IsSelfDeafened.Value;
if (model.IsSelfMuted != null) if (model.IsSelfMuted != null)
@@ -173,14 +180,16 @@ namespace Discord
if (model.IsServerSuppressed != null) if (model.IsServerSuppressed != null)
IsServerSuppressed = model.IsServerSuppressed.Value; IsServerSuppressed = model.IsServerSuppressed.Value;
} }
private void UpdateRoles(string[] roleIds)
private void UpdateRoles(IEnumerable<Role> 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) internal void UpdateActivity(DateTime? activity = null)
@@ -191,7 +200,7 @@ namespace Discord
internal void UpdateChannelPermissions(Channel channel) 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; var server = Server;
if (server == null || channel.Server != server) return; if (server == null || channel.Server != server) return;
@@ -225,14 +234,14 @@ namespace Discord
if (newPermissions != oldPermissions) if (newPermissions != oldPermissions)
{ {
permissions.SetRawValueInternal(newPermissions); permissions.SetRawValueInternal(newPermissions);
channel.InvalidMembersCache();
channel.InvalidateMembersCache();
} }


permissions.SetRawValueInternal(newPermissions); permissions.SetRawValueInternal(newPermissions);
} }
internal void UpdateServerPermissions() 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; var server = Server;
if (server == null) return; if (server == null) return;
@@ -290,7 +299,7 @@ namespace Discord
{ {
if (role == null) throw new ArgumentNullException(nameof(role)); if (role == null) throw new ArgumentNullException(nameof(role));


return _roleIds.Contains(role.Id);
return _roles.ContainsKey(role.Id);
} }
} }
} }

+ 5
- 5
src/Discord.Net/Net/WebSockets/WebSocket.cs View File

@@ -53,21 +53,21 @@ namespace Discord.Net.WebSockets
_connectedEvent = new ManualResetEventSlim(false); _connectedEvent = new ManualResetEventSlim(false);
_engine = new WebSocketSharpEngine(this, client.Config); _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 compressed = new MemoryStream(e.Data, 2, e.Data.Length - 2))
using (var decompressed = new MemoryStream()) using (var decompressed = new MemoryStream())
{ {
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress)) using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
await zlib.CopyToAsync(decompressed);
zlib.CopyTo(decompressed);
decompressed.Position = 0; decompressed.Position = 0;
using (var reader = new StreamReader(decompressed)) 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();
}; };
} }




Loading…
Cancel
Save