Browse Source

Several memory and performance improvements

tags/1.0-rc
RogueException 9 years ago
parent
commit
3684548769
20 changed files with 73 additions and 52 deletions
  1. +1
    -1
      src/Discord.Net/API/Common/GuildMember.cs
  2. +1
    -1
      src/Discord.Net/API/Common/Integration.cs
  3. +1
    -1
      src/Discord.Net/API/Common/InviteMetadata.cs
  4. +2
    -2
      src/Discord.Net/API/Common/Message.cs
  5. +1
    -1
      src/Discord.Net/API/Gateway/ExtendedGuild.cs
  6. +18
    -14
      src/Discord.Net/DiscordSocketClient.cs
  7. +4
    -2
      src/Discord.Net/Entities/Guilds/GuildIntegration.cs
  8. +2
    -1
      src/Discord.Net/Entities/Guilds/IGuildIntegration.cs
  9. +1
    -1
      src/Discord.Net/Entities/ISnowflakeEntity.cs
  10. +1
    -1
      src/Discord.Net/Entities/Invites/IInviteMetadata.cs
  11. +5
    -2
      src/Discord.Net/Entities/Invites/InviteMetadata.cs
  12. +2
    -2
      src/Discord.Net/Entities/Messages/IMessage.cs
  13. +6
    -4
      src/Discord.Net/Entities/Messages/Message.cs
  14. +1
    -1
      src/Discord.Net/Entities/SnowflakeEntity.cs
  15. +5
    -3
      src/Discord.Net/Entities/Users/GuildUser.cs
  16. +1
    -1
      src/Discord.Net/Entities/Users/IGuildUser.cs
  17. +4
    -3
      src/Discord.Net/Entities/Users/User.cs
  18. +9
    -1
      src/Discord.Net/Entities/WebSocket/CachedGuild.cs
  19. +2
    -1
      src/Discord.Net/Extensions/CollectionExtensions.cs
  20. +6
    -9
      src/Discord.Net/Utilities/DateTimeUtils.cs

+ 1
- 1
src/Discord.Net/API/Common/GuildMember.cs View File

@@ -12,7 +12,7 @@ namespace Discord.API
[JsonProperty("roles")]
public ulong[] Roles { get; set; }
[JsonProperty("joined_at")]
public DateTime JoinedAt { get; set; }
public DateTimeOffset JoinedAt { get; set; }
[JsonProperty("deaf")]
public bool Deaf { get; set; }
[JsonProperty("mute")]


+ 1
- 1
src/Discord.Net/API/Common/Integration.cs View File

@@ -26,6 +26,6 @@ namespace Discord.API
[JsonProperty("account")]
public IntegrationAccount Account { get; set; }
[JsonProperty("synced_at")]
public DateTime SyncedAt { get; set; }
public DateTimeOffset SyncedAt { get; set; }
}
}

+ 1
- 1
src/Discord.Net/API/Common/InviteMetadata.cs View File

@@ -16,7 +16,7 @@ namespace Discord.API
[JsonProperty("temporary")]
public bool Temporary { get; set; }
[JsonProperty("created_at")]
public DateTime CreatedAt { get; set; }
public DateTimeOffset CreatedAt { get; set; }
[JsonProperty("revoked")]
public bool Revoked { get; set; }
}


+ 2
- 2
src/Discord.Net/API/Common/Message.cs View File

@@ -14,9 +14,9 @@ namespace Discord.API
[JsonProperty("content")]
public Optional<string> Content { get; set; }
[JsonProperty("timestamp")]
public Optional<DateTime> Timestamp { get; set; }
public Optional<DateTimeOffset> Timestamp { get; set; }
[JsonProperty("edited_timestamp")]
public Optional<DateTime?> EditedTimestamp { get; set; }
public Optional<DateTimeOffset?> EditedTimestamp { get; set; }
[JsonProperty("tts")]
public Optional<bool> IsTextToSpeech { get; set; }
[JsonProperty("mention_everyone")]


+ 1
- 1
src/Discord.Net/API/Gateway/ExtendedGuild.cs View File

@@ -19,6 +19,6 @@ namespace Discord.API.Gateway
[JsonProperty("channels")]
public Channel[] Channels { get; set; }
[JsonProperty("joined_at")]
public DateTime JoinedAt { get; set; }
public DateTimeOffset JoinedAt { get; set; }
}
}

+ 18
- 14
src/Discord.Net/DiscordSocketClient.cs View File

@@ -10,7 +10,6 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -38,6 +37,7 @@ namespace Discord
public event Func<ISelfUser, ISelfUser, Task> CurrentUserUpdated;
public event Func<IChannel, IUser, Task> UserIsTyping;
public event Func<int, Task> LatencyUpdated;
//TODO: Add PresenceUpdated? VoiceStateUpdated?

private readonly ConcurrentQueue<ulong> _largeGuilds;
private readonly Logger _gatewayLogger;
@@ -50,6 +50,7 @@ namespace Discord
private readonly bool _enablePreUpdateEvents;
private readonly int _largeThreshold;
private readonly int _totalShards;
private ConcurrentHashSet<ulong> _dmChannels;
private string _sessionId;
private int _lastSeq;
private ImmutableDictionary<string, VoiceRegion> _voiceRegions;
@@ -71,20 +72,14 @@ namespace Discord
internal DataStore DataStore { get; private set; }

internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
internal IReadOnlyCollection<CachedGuild> Guilds
{
get
{
var guilds = DataStore.Guilds;
return guilds.ToReadOnlyCollection(guilds);
}
}
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
internal IReadOnlyCollection<CachedDMChannel> DMChannels
{
get
{
var users = DataStore.Users;
return users.Select(x => x.DMChannel).Where(x => x != null).ToReadOnlyCollection(users);
var dmChannels = _dmChannels;
var store = DataStore;
return dmChannels.Select(x => store.GetChannel(x) as CachedDMChannel).Where(x => x != null).ToReadOnlyCollection(dmChannels);
}
}
internal IReadOnlyCollection<VoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();
@@ -136,6 +131,7 @@ namespace Discord

_voiceRegions = ImmutableDictionary.Create<string, VoiceRegion>();
_largeGuilds = new ConcurrentQueue<ulong>();
_dmChannels = new ConcurrentHashSet<ulong>();
}

protected override async Task OnLoginAsync()
@@ -305,11 +301,16 @@ namespace Discord
{
return Task.FromResult<IChannel>(DataStore.GetChannel(id));
}
internal CachedDMChannel AddDMChannel(API.Channel model, DataStore dataStore)
public override Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync()
{
return Task.FromResult<IReadOnlyCollection<IDMChannel>>(DMChannels);
}
internal CachedDMChannel AddDMChannel(API.Channel model, DataStore dataStore, ConcurrentHashSet<ulong> dmChannels)
{
var recipient = GetOrAddUser(model.Recipient.Value, dataStore);
var channel = recipient.AddDMChannel(model);
dataStore.AddChannel(channel);
dmChannels.TryAdd(model.Id);
return channel;
}
internal CachedDMChannel RemoveDMChannel(ulong id)
@@ -317,6 +318,7 @@ namespace Discord
var dmChannel = DataStore.RemoveChannel(id) as CachedDMChannel;
var recipient = dmChannel.Recipient;
recipient.RemoveDMChannel(id);
_dmChannels.TryRemove(id);
return dmChannel;
}

@@ -455,6 +457,7 @@ namespace Discord
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
var dataStore = _dataStoreProvider(ShardId, _totalShards, data.Guilds.Length, data.PrivateChannels.Length);
var dmChannels = new ConcurrentHashSet<ulong>();

var currentUser = new CachedSelfUser(this, data.User);
//dataStore.GetOrAddUser(data.User.Id, _ => currentUser);
@@ -462,10 +465,11 @@ namespace Discord
for (int i = 0; i < data.Guilds.Length; i++)
AddGuild(data.Guilds[i], dataStore);
for (int i = 0; i < data.PrivateChannels.Length; i++)
AddDMChannel(data.PrivateChannels[i], dataStore);
AddDMChannel(data.PrivateChannels[i], dataStore, dmChannels);

_sessionId = data.SessionId;
_currentUser = currentUser;
_dmChannels = dmChannels;
DataStore = dataStore;

await Ready.RaiseAsync().ConfigureAwait(false);
@@ -577,7 +581,7 @@ namespace Discord
}
}
else
channel = AddDMChannel(data, DataStore);
channel = AddDMChannel(data, DataStore, _dmChannels);
if (channel != null)
await ChannelCreated.RaiseAsync(channel).ConfigureAwait(false);
}


+ 4
- 2
src/Discord.Net/Entities/Guilds/GuildIntegration.cs View File

@@ -9,13 +9,14 @@ namespace Discord
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
internal class GuildIntegration : Entity<ulong>, IGuildIntegration
{
private long _syncedAtTicks;

public string Name { get; private set; }
public string Type { get; private set; }
public bool IsEnabled { get; private set; }
public bool IsSyncing { get; private set; }
public ulong ExpireBehavior { get; private set; }
public ulong ExpireGracePeriod { get; private set; }
public DateTime SyncedAt { get; private set; }

public Guild Guild { get; private set; }
public Role Role { get; private set; }
@@ -23,6 +24,7 @@ namespace Discord
public IntegrationAccount Account { get; private set; }

public override DiscordClient Discord => Guild.Discord;
public DateTimeOffset SyncedAt => DateTimeUtils.FromTicks(_syncedAtTicks);

public GuildIntegration(Guild guild, Model model)
: base(model.Id)
@@ -41,7 +43,7 @@ namespace Discord
IsSyncing = model.Syncing;
ExpireBehavior = model.ExpireBehavior;
ExpireGracePeriod = model.ExpireGracePeriod;
SyncedAt = model.SyncedAt;
_syncedAtTicks = model.SyncedAt.UtcTicks;

Role = Guild.GetRole(model.RoleId);
User = new User(Discord, model.User);


+ 2
- 1
src/Discord.Net/Entities/Guilds/IGuildIntegration.cs View File

@@ -2,6 +2,7 @@

namespace Discord
{
//TODO: Add docstrings
public interface IGuildIntegration
{
ulong Id { get; }
@@ -11,7 +12,7 @@ namespace Discord
bool IsSyncing { get; }
ulong ExpireBehavior { get; }
ulong ExpireGracePeriod { get; }
DateTime SyncedAt { get; }
DateTimeOffset SyncedAt { get; }
IntegrationAccount Account { get; }

IGuild Guild { get; }


+ 1
- 1
src/Discord.Net/Entities/ISnowflakeEntity.cs View File

@@ -5,6 +5,6 @@ namespace Discord
public interface ISnowflakeEntity : IEntity<ulong>
{
/// <summary> Gets when this object was created. </summary>
DateTime CreatedAt { get; }
DateTimeOffset CreatedAt { get; }
}
}

+ 1
- 1
src/Discord.Net/Entities/Invites/IInviteMetadata.cs View File

@@ -17,6 +17,6 @@ namespace Discord
/// <summary> Gets the amount of times this invite has been used. </summary>
int Uses { get; }
/// <summary> Gets when this invite was created. </summary>
DateTime CreatedAt { get; }
DateTimeOffset CreatedAt { get; }
}
}

+ 5
- 2
src/Discord.Net/Entities/Invites/InviteMetadata.cs View File

@@ -5,14 +5,17 @@ namespace Discord
{
internal class InviteMetadata : Invite, IInviteMetadata
{
private long _createdAtTicks;

public bool IsRevoked { get; private set; }
public bool IsTemporary { get; private set; }
public int? MaxAge { get; private set; }
public int? MaxUses { get; private set; }
public int Uses { get; private set; }
public DateTime CreatedAt { get; private set; }
public IUser Inviter { get; private set; }

public DateTimeOffset CreatedAt => DateTimeUtils.FromTicks(_createdAtTicks);

public InviteMetadata(DiscordClient client, Model model)
: base(client, model)
{
@@ -28,7 +31,7 @@ namespace Discord
MaxAge = model.MaxAge != 0 ? model.MaxAge : (int?)null;
MaxUses = model.MaxUses;
Uses = model.Uses;
CreatedAt = model.CreatedAt;
_createdAtTicks = model.CreatedAt.UtcTicks;
}
}
}

+ 2
- 2
src/Discord.Net/Entities/Messages/IMessage.cs View File

@@ -8,7 +8,7 @@ namespace Discord
public interface IMessage : IDeletable, ISnowflakeEntity, IUpdateable
{
/// <summary> Gets the time of this message's last edit, if any. </summary>
DateTime? EditedTimestamp { get; }
DateTimeOffset? EditedTimestamp { get; }
/// <summary> Returns true if this message was sent as a text-to-speech message. </summary>
bool IsTTS { get; }
/// <summary> Returns the original, unprocessed text for this message. </summary>
@@ -16,7 +16,7 @@ namespace Discord
/// <summary> Returns the text for this message after mention processing. </summary>
string Text { get; }
/// <summary> Gets the time this message was sent. </summary>
DateTime Timestamp { get; }
DateTimeOffset Timestamp { get; }

/// <summary> Gets the channel this message was sent to. </summary>
IMessageChannel Channel { get; }


+ 6
- 4
src/Discord.Net/Entities/Messages/Message.cs View File

@@ -12,12 +12,12 @@ namespace Discord
internal class Message : SnowflakeEntity, IMessage
{
private bool _isMentioningEveryone;
private long _timestampTicks;
private long? _editedTimestampTicks;

public DateTime? EditedTimestamp { get; private set; }
public bool IsTTS { get; private set; }
public string RawText { get; private set; }
public string Text { get; private set; }
public DateTime Timestamp { get; private set; }
public IMessageChannel Channel { get; }
public IUser Author { get; }
@@ -29,6 +29,8 @@ namespace Discord
public ImmutableArray<User> MentionedUsers { get; private set; }

public override DiscordClient Discord => (Channel as Entity<ulong>).Discord;
public DateTimeOffset? EditedTimestamp => DateTimeUtils.FromTicks(_editedTimestampTicks);
public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks);

public Message(IMessageChannel channel, IUser author, Model model)
: base(model.Id)
@@ -56,9 +58,9 @@ namespace Discord
if (model.IsTextToSpeech.IsSpecified)
IsTTS = model.IsTextToSpeech.Value;
if (model.Timestamp.IsSpecified)
Timestamp = model.Timestamp.Value;
_timestampTicks = model.Timestamp.Value.UtcTicks;
if (model.EditedTimestamp.IsSpecified)
EditedTimestamp = model.EditedTimestamp.Value;
_editedTimestampTicks = model.EditedTimestamp.Value?.UtcTicks;
if (model.IsMentioningEveryone.IsSpecified)
_isMentioningEveryone = model.IsMentioningEveryone.Value;


+ 1
- 1
src/Discord.Net/Entities/SnowflakeEntity.cs View File

@@ -5,7 +5,7 @@ namespace Discord
internal abstract class SnowflakeEntity : Entity<ulong>, ISnowflakeEntity
{
//TODO: C#7 Candidate for Extension Property. Lets us remove this class.
public DateTime CreatedAt => DateTimeUtils.FromSnowflake(Id);
public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id);

public SnowflakeEntity(ulong id)
: base(id)


+ 5
- 3
src/Discord.Net/Entities/Users/GuildUser.cs View File

@@ -14,9 +14,10 @@ namespace Discord
[DebuggerDisplay("{DebuggerDisplay,nq}")]
internal class GuildUser : IGuildUser, ISnowflakeEntity
{
private long? _joinedAtTicks;

public bool IsDeaf { get; private set; }
public bool IsMute { get; private set; }
public DateTime? JoinedAt { get; private set; }
public string Nickname { get; private set; }
public GuildPermissions GuildPermissions { get; private set; }

@@ -26,7 +27,7 @@ namespace Discord

public ulong Id => User.Id;
public string AvatarUrl => User.AvatarUrl;
public DateTime CreatedAt => User.CreatedAt;
public DateTimeOffset CreatedAt => User.CreatedAt;
public string Discriminator => User.Discriminator;
public bool IsAttached => User.IsAttached;
public bool IsBot => User.IsBot;
@@ -36,6 +37,7 @@ namespace Discord
public virtual Game? Game => User.Game;

public DiscordClient Discord => Guild.Discord;
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);

public GuildUser(Guild guild, User user)
{
@@ -62,7 +64,7 @@ namespace Discord
//if (model.Mute.IsSpecified)
IsMute = model.Mute;
//if (model.JoinedAt.IsSpecified)
JoinedAt = model.JoinedAt;
_joinedAtTicks = model.JoinedAt.UtcTicks;
if (model.Nick.IsSpecified)
Nickname = model.Nick.Value;



+ 1
- 1
src/Discord.Net/Entities/Users/IGuildUser.cs View File

@@ -13,7 +13,7 @@ namespace Discord
/// <summary> Returns true if the guild has muted this user. </summary>
bool IsMute { get; }
/// <summary> Gets when this user joined this guild. </summary>
DateTime? JoinedAt { get; }
DateTimeOffset? JoinedAt { get; }
/// <summary> Gets the nickname for this user. </summary>
string Nickname { get; }
/// <summary> Gets the guild-level permissions granted to this user by their roles. </summary>


+ 4
- 3
src/Discord.Net/Entities/Users/User.cs View File

@@ -9,14 +9,15 @@ namespace Discord
internal class User : SnowflakeEntity, IUser
{
private string _avatarId;
public string Discriminator { get; private set; }
private ushort _discriminator;
public bool IsBot { get; private set; }
public string Username { get; private set; }

public override DiscordClient Discord { get; }

public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId);
public string Discriminator => _discriminator.ToString("D4");
public string Mention => MentionUtils.Mention(this, false);
public string NicknameMention => MentionUtils.Mention(this, true);
public virtual Game? Game => null;
@@ -33,7 +34,7 @@ namespace Discord
if (source == UpdateSource.Rest && IsAttached) return;

_avatarId = model.Avatar;
Discriminator = model.Discriminator;
_discriminator = ushort.Parse(model.Discriminator);
IsBot = model.Bot;
Username = model.Username;
}


+ 9
- 1
src/Discord.Net/Entities/WebSocket/CachedGuild.cs View File

@@ -32,7 +32,15 @@ namespace Discord

public new DiscordSocketClient Discord => base.Discord as DiscordSocketClient;
public CachedGuildUser CurrentUser => GetUser(Discord.CurrentUser.Id);
public IReadOnlyCollection<ICachedGuildChannel> Channels => _channels.Select(x => GetChannel(x)).ToReadOnlyCollection(_channels);
public IReadOnlyCollection<ICachedGuildChannel> Channels
{
get
{
var channels = _channels;
var store = Discord.DataStore;
return channels.Select(x => store.GetChannel(x) as ICachedGuildChannel).Where(x => x != null).ToReadOnlyCollection(channels);
}
}
public IReadOnlyCollection<CachedGuildUser> Members => _members.ToReadOnlyCollection();

public CachedGuild(DiscordSocketClient discord, ExtendedModel model, DataStore dataStore) : base(discord, model)


+ 2
- 1
src/Discord.Net/Extensions/CollectionExtensions.cs View File

@@ -11,12 +11,13 @@ namespace Discord.Extensions
public static IReadOnlyCollection<TValue> ToReadOnlyCollection<TValue, TSource>(this IEnumerable<TValue> query, IReadOnlyCollection<TSource> source)
=> new ConcurrentDictionaryWrapper<TValue, TSource>(source, query);
}
internal struct ConcurrentDictionaryWrapper<TValue, TSource> : IReadOnlyCollection<TValue>
{
private readonly IReadOnlyCollection<TSource> _source;
private readonly IEnumerable<TValue> _query;

//It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected
public int Count => _source.Count;
public ConcurrentDictionaryWrapper(IReadOnlyCollection<TSource> source, IEnumerable<TValue> query)


+ 6
- 9
src/Discord.Net/Utilities/DateTimeUtils.cs View File

@@ -4,15 +4,12 @@ namespace Discord
{
internal static class DateTimeUtils
{
private const ulong EpochTicks = 621355968000000000UL;
private const ulong DiscordEpochMillis = 1420070400000UL;
public static DateTimeOffset FromSnowflake(ulong value)
=> DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL));

public static DateTime FromEpochMilliseconds(ulong value)
=> new DateTime((long)(value * TimeSpan.TicksPerMillisecond + EpochTicks), DateTimeKind.Utc);
public static DateTime FromEpochSeconds(ulong value)
=> new DateTime((long)(value * TimeSpan.TicksPerSecond + EpochTicks), DateTimeKind.Utc);

public static DateTime FromSnowflake(ulong value)
=> FromEpochMilliseconds((value >> 22) + DiscordEpochMillis);
public static DateTimeOffset FromTicks(long ticks)
=> new DateTimeOffset(ticks, TimeSpan.Zero);
public static DateTimeOffset? FromTicks(long? ticks)
=> ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null;
}
}

Loading…
Cancel
Save