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)
{
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);
}

/// <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();

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) { }
}
}

+ 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>
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));
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;
}



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

@@ -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));
}
}
}

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

@@ -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;
}
}


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

@@ -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) { }


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

@@ -7,11 +7,13 @@ namespace Discord
{
internal sealed class Roles : AsyncCollection<Role>
{
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));


+ 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>
public IEnumerable<Server> AllServers => _servers.Where(x => !x.IsVirtual);
public IEnumerable<Server> AllServers => _servers;
internal Servers Servers => _servers;
private readonly Servers _servers;



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

@@ -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<ReadyEvent>(_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


+ 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;
}
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<string>("token");
_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;
@@ -319,7 +319,6 @@ namespace Discord
{
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
{
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);
}
}
}

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

@@ -34,7 +34,10 @@ namespace Discord
_users = new ConcurrentDictionary<string, User>();
}
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);


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

@@ -5,10 +5,11 @@ using Newtonsoft.Json;
namespace Discord
{
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>
public int MaxAge { get; private set; }
/// <summary> The amount of times this invite has been used. </summary>
@@ -19,47 +20,72 @@ namespace Discord
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>
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>
[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>
[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>
[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)


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

@@ -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)


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

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

/// <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>
[JsonIgnore]
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)
: 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);


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

@@ -8,21 +8,11 @@ using System.Linq;
namespace Discord
{
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>
public string Name { get; private set; }
/// <summary> Returns the current logged-in user's data for this server. </summary>
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>
public int AFKTimeout { get; private set; }
@@ -38,49 +28,49 @@ namespace Discord
/// <summary> Returns the user that first created this server. </summary>
[JsonIgnore]
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>
[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>
[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>
[JsonIgnore]
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>
[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>
[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>
[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>
[JsonIgnore]
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>
[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>
[JsonIgnore]
public Role EveryoneRole => _client.Roles[EveryoneRoleId];
public Role EveryoneRole { get; private set; }
/// <summary> Returns a collection of all roles within this server. </summary>
[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)
: 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;
}
}
}
}

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

@@ -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<string, Channel> _channels;
private ConcurrentDictionary<string, ChannelPermissions> _permissions;
private ServerPermissions _serverPermissions;
private string[] _roleIds;

/// <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>
public string Name { get; private set; }
/// <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;

[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<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>
[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>
[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)
: base(client, id)
{
ServerId = serverId;
_serverId = serverId;
Status = UserStatus.Offline;
_roleIds = _initialRoleIds;
//_roles = new Dictionary<string, Role>();
_channels = new ConcurrentDictionary<string, Channel>();
_permissions = new ConcurrentDictionary<string, ChannelPermissions>();
_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<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)
@@ -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);
}
}
}

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

@@ -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();
};
}



Loading…
Cancel
Save