Browse Source

Add new reference system

tags/docs-0.9
RogueException 9 years ago
parent
commit
674c585bee
10 changed files with 205 additions and 164 deletions
  1. +3
    -0
      src/Discord.Net.Net45/Discord.Net.csproj
  2. +68
    -0
      src/Discord.Net/Helpers/Reference.cs
  3. +6
    -4
      src/Discord.Net/Models/CachedObject.cs
  4. +27
    -28
      src/Discord.Net/Models/Channel.cs
  5. +2
    -2
      src/Discord.Net/Models/GlobalUser.cs
  6. +15
    -36
      src/Discord.Net/Models/Invite.cs
  7. +14
    -26
      src/Discord.Net/Models/Message.cs
  8. +11
    -18
      src/Discord.Net/Models/Role.cs
  9. +20
    -10
      src/Discord.Net/Models/Server.cs
  10. +39
    -40
      src/Discord.Net/Models/User.cs

+ 3
- 0
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -223,6 +223,9 @@
<Compile Include="..\Discord.Net\Helpers\Mention.cs">
<Link>Helpers\Mention.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\Helpers\Reference.cs">
<Link>Helpers\Reference.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\Helpers\TaskHelper.cs">
<Link>Helpers\TaskHelper.cs</Link>
</Compile>


+ 68
- 0
src/Discord.Net/Helpers/Reference.cs View File

@@ -0,0 +1,68 @@
using System;

namespace Discord
{
internal class Reference<T>
where T : CachedObject
{
private Action<T> _onCache, _onUncache;
private Func<string, T> _getItem;
private string _id;
public string Id
{
get { return _id; }
set
{
_id = value;
_value = null;
}
}

private T _value;
public T Value
{
get
{
var v = _value; //A little trickery to make this threadsafe
if (v != null && !_value.IsCached)
{
v = null;
_value = null;
}
if (v == null && _id != null)
{
v = _getItem(_id);
if (v != null)
_onCache(v);
_value = v;
}
return v;
}
}

public T Load()
{
return Value; //Used for precaching
}

public void Unload()
{
if (_onUncache != null)
{
var v = _value;
if (v != null && _onUncache != null)
_onUncache(v);
}
}

public Reference(Func<string, T> onUpdate, Action<T> onCache = null, Action<T> onUncache = null)
: this(null, onUpdate, onCache, onUncache) { }
public Reference(string id, Func<string, T> getItem, Action<T> onCache = null, Action<T> onUncache = null)
{
_id = id;
_getItem = getItem;
_onCache = onCache;
_onUncache = onUncache;
}
}
}

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

@@ -5,6 +5,8 @@
protected readonly DiscordClient _client;
private bool _isCached;

internal bool IsCached => _isCached;

internal CachedObject(DiscordClient client, string id)
{
_client = client;
@@ -18,18 +20,18 @@

internal void Cache()
{
OnCached();
LoadReferences();
_isCached = true;
}
internal void Uncache()
{
if (_isCached)
{
OnUncached();
UnloadReferences();
_isCached = false;
}
}
internal abstract void OnCached();
internal abstract void OnUncached();
internal abstract void LoadReferences();
internal abstract void UnloadReferences();
}
}

+ 27
- 28
src/Discord.Net/Models/Channel.cs View File

@@ -33,19 +33,19 @@ namespace Discord
/// <summary> Returns the position of this channel in the channel list for this server. </summary>
public int Position { get; private set; }
/// <summary> Returns false is this is a public chat and true if this is a private chat with another user (see Recipient). </summary>
public bool IsPrivate => _recipientId != null;
public bool IsPrivate => _recipient.Id != null;
/// <summary> Returns the type of this channel (see ChannelTypes). </summary>
public string Type { get; private set; }

/// <summary> Returns the server containing this channel. </summary>
[JsonIgnore]
public Server Server { get; private set; }
private readonly string _serverId;
public Server Server => _server.Value;
private readonly Reference<Server> _server;

/// For private chats, returns the target user, otherwise null.
[JsonIgnore]
public User Recipient { get; private set; }
private readonly string _recipientId;
public User Recipient => _recipient.Value;
private readonly Reference<User> _recipient;
/// <summary> Returns a collection of all users with read access to this channel. </summary>
[JsonIgnore]
@@ -74,39 +74,35 @@ namespace Discord
internal Channel(DiscordClient client, string id, string serverId, string recipientId)
: base(client, id)
{
_serverId = serverId;
_recipientId = recipientId;
_server = new Reference<Server>(serverId,
x => _client.Servers[x],
x => x.AddChannel(this),
x => x.RemoveChannel(this));
_recipient = new Reference<User>(recipientId,
x => _client.Users[x, _server.Id],
x =>
{
Name = "@" + x.Name;
x.GlobalUser.PrivateChannel = this;
},
x => x.GlobalUser.PrivateChannel = null);
_permissionOverwrites = _initialPermissionsOverwrites;
_areMembersStale = true;

//Local Cache
_messages = new ConcurrentDictionary<string, Message>();
}
internal override void OnCached()
internal override void LoadReferences()
{
if (IsPrivate)
{
var recipient = _client.Users[_recipientId, null];
Name = "@" + recipient.Name;
recipient.GlobalUser.PrivateChannel = this;
Recipient = recipient;
}
_recipient.Load();
else
{
var server = _client.Servers[_serverId];
server.AddChannel(this);
Server = server;
}
_server.Load();
}
internal override void OnUncached()
internal override void UnloadReferences()
{
var server = Server;
if (server != null)
server.RemoveChannel(this);
var recipient = Recipient;
if (recipient != null)
recipient.GlobalUser.PrivateChannel = null;
_server.Unload();
_recipient.Unload();
var globalMessages = _client.Messages;
var messages = _messages;
@@ -167,7 +163,10 @@ namespace Discord
}
private void UpdateMembersCache()
{
_members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToDictionary(x => x.Id, x => x);
if (_server.Id != null)
_members = Server.Members.Where(x => x.GetPermissions(this)?.ReadMessages ?? false).ToDictionary(x => x.Id, x => x);
else
_members = new Dictionary<string, User>();
_areMembersStale = false;
}



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

@@ -43,8 +43,8 @@ namespace Discord
{
_users = new ConcurrentDictionary<string, User>();
}
internal override void OnCached() { }
internal override void OnUncached()
internal override void LoadReferences() { }
internal override void UnloadReferences()
{
//Don't need to clean _users - they're considered owned by server
}


+ 15
- 36
src/Discord.Net/Models/Invite.cs View File

@@ -21,58 +21,37 @@ namespace Discord

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

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

/// <summary> Returns the channel this invite is to. </summary>
[JsonIgnore]
public Channel Channel { get; private set; }
[JsonProperty("ChannelId")]
private readonly string _channelId;
public Channel Channel => _channel.Value;
private readonly Reference<Channel> _channel;

internal Invite(DiscordClient client, string code, string xkcdPass, string serverId, string inviterId, string channelId)
: base(client, code)
{
XkcdCode = xkcdPass;
_serverId = serverId;
_inviterId = inviterId;
_channelId = channelId;
_server = new Reference<Server>(serverId, x => _client.Servers[x] ?? new Server(client, x));
_inviter = new Reference<User>(serverId, x => _client.Users[x, _server.Id] ?? new User(client, x, _server.Id));
_channel = new Reference<Channel>(serverId, x => _client.Channels[x] ?? new Channel(client, x, _server.Id, null));
}

internal override void OnCached()
internal override void LoadReferences()
{
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;
}

if (_channelId != null)
{
var channel = _client.Channels[_channelId];
if (channel == null)
channel = new Channel(_client, _channelId, _serverId, null);
Channel = channel;
}
_server.Load();
_inviter.Load();
_channel.Load();
}
internal override void OnUncached() { }
internal override void UnloadReferences() { }

public override string ToString() => XkcdCode ?? Id;


+ 14
- 26
src/Discord.Net/Models/Message.cs View File

@@ -129,48 +129,36 @@ namespace Discord
/// <summary> Returns the server containing the channel this message was sent to. </summary>
[JsonIgnore]
public Server Server => Channel.Server;
public Server Server => _channel.Value.Server;
/// <summary> Returns the channel this message was sent to. </summary>
[JsonIgnore]
public Channel Channel { get; private set; }
private readonly string _channelId;
public Channel Channel => _channel.Value;
private readonly Reference<Channel> _channel;

/// <summary> Returns true if the current user created this message. </summary>
public bool IsAuthor => _client.CurrentUserId == _userId;
public bool IsAuthor => _client.CurrentUserId == _user.Id;
/// <summary> Returns the author of this message. </summary>
[JsonIgnore]
public User User { get; private set; }
private readonly string _userId;
public User User => _user.Value;
private readonly Reference<User> _user;

internal Message(DiscordClient client, string id, string channelId, string userId)
: base(client, id)
{
_channelId = channelId;
_userId = userId;
_channel = new Reference<Channel>(channelId, x => _client.Channels[x], x => x.AddMessage(this), x => x.RemoveMessage(this));
_user = new Reference<User>(userId, x => _client.Users[x]);
Attachments = _initialAttachments;
Embeds = _initialEmbeds;
}
internal override void OnCached()
internal override void LoadReferences()
{
//References
var channel = _client.Channels[_channelId];
channel.AddMessage(this);
Channel = channel;

var user = _client.Users[_userId, channel.Server?.Id];
//user.AddMessage(this);
User = user;
_channel.Load();
_user.Load();
}
internal override void OnUncached()
internal override void UnloadReferences()
{
//References
var channel = Channel;
if (channel != null)
channel.RemoveMessage(this);

/*var user = User;
if (user != null)
user.RemoveMessage(this);*/
_channel.Unload();
_user.Unload();
}

internal void Update(MessageInfo model)


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

@@ -6,9 +6,7 @@ using System.Linq;
namespace Discord
{
public sealed class Role : CachedObject
{
private readonly string _serverId;
{
/// <summary> Returns the name of this role. </summary>
public string Name { get; private set; }
/// <summary> If true, this role is displayed isolated from other users. </summary>
@@ -22,40 +20,35 @@ namespace Discord

/// <summary> Returns the the permissions contained by this role. </summary>
public ServerPermissions Permissions { get; }
/// <summary> Returns the server this role is a member of. </summary>
[JsonIgnore]
public Server Server { get; private set; }
public Server Server => _server.Value;
private readonly Reference<Server> _server;

/// <summary> Returns true if this is the role representing all users in a server. </summary>
public bool IsEveryone => _serverId == null || Id == _serverId;
public bool IsEveryone => _server.Id == null || Id == _server.Id;
/// <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));
public IEnumerable<User> Members => _server.Id != null ? (IsEveryone ? Server.Members : Server.Members.Where(x => x.HasRole(this))) : new User[0];
//TODO: Add local members cache

internal Role(DiscordClient client, string id, string serverId)
: base(client, id)
{
_serverId = serverId;
_server = new Reference<Server>(serverId, x => _client.Servers[x], x => x.AddRole(this), x => x.RemoveRole(this));
Permissions = new ServerPermissions(0);
Permissions.Lock();
Color = new Color(0);
Color.Lock();
}
internal override void OnCached()
internal override void LoadReferences()
{
//References
var server = _client.Servers[_serverId];
server.AddRole(this);
Server = server;
_server.Load();
}
internal override void OnUncached()
internal override void UnloadReferences()
{
//References
var server = Server;
if (server != null)
server.RemoveRole(this);
_server.Unload();
}

internal void Update(RoleInfo model)


+ 20
- 10
src/Discord.Net/Models/Server.cs View File

@@ -84,8 +84,8 @@ namespace Discord
_bans = new ConcurrentDictionary<string, bool>();
_invites = new ConcurrentDictionary<string, Invite>();
}
internal override void OnCached() { }
internal override void OnUncached()
internal override void LoadReferences() { }
internal override void UnloadReferences()
{
//Global Cache
var globalChannels = _client.Channels;
@@ -210,21 +210,31 @@ namespace Discord

internal void AddMember(User member)
{
_members.TryAdd(member.Id, member);
foreach (var channel in Channels)
if (_members.TryAdd(member.Id, member))
{
member.AddChannel(channel);
channel.InvalidatePermissionsCache(member);
if (member.Id == _ownerId)
Owner = member;

foreach (var channel in Channels)
{
member.AddChannel(channel);
channel.InvalidatePermissionsCache(member);
}
}
}
internal void RemoveMember(User member)
{
foreach (var channel in Channels)
if (_members.TryRemove(member.Id, out member))
{
member.RemoveChannel(channel);
channel.InvalidatePermissionsCache(member);
if (member.Id == _ownerId)
Owner = null;

foreach (var channel in Channels)
{
member.RemoveChannel(channel);
channel.InvalidatePermissionsCache(member);
}
}
_members.TryRemove(member.Id, out member);
}
internal void HasMember(User user) => _members.ContainsKey(user.Id);



+ 39
- 40
src/Discord.Net/Models/User.cs View File

@@ -16,7 +16,7 @@ namespace Discord
private ServerPermissions _serverPermissions;

/// <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, _server.Id);
/// <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>
@@ -49,11 +49,12 @@ namespace Discord
private DateTime _lastOnline;

[JsonIgnore]
internal GlobalUser GlobalUser { get; private set; }
internal GlobalUser GlobalUser => _globalUser.Value;
private readonly Reference<GlobalUser> _globalUser;

[JsonIgnore]
public Server Server { get; private set; }
private string _serverId;
public Server Server => _server.Value;
private readonly Reference<Server> _server;

[JsonIgnore]
public Channel VoiceChannel { get; private set; }
@@ -64,7 +65,7 @@ namespace Discord

/// <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.User.Id == Id && x.Server.Id == _serverId);
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.User.Id == Id && x.Server.Id == _server.Id);

/// <summary> Returns a collection of all channels this user is a member of. </summary>
[JsonIgnore]
@@ -73,44 +74,41 @@ namespace Discord
internal User(DiscordClient client, string id, string serverId)
: base(client, id)
{
_serverId = serverId;
_globalUser = new Reference<GlobalUser>(id,
x => _client.GlobalUsers.GetOrAdd(x),
x => x.AddUser(this),
x => x.RemoveUser(this));
_server = new Reference<Server>(serverId,
x => _client.Servers[x],
x =>
{
x.AddMember(this);
if (x.Id == _client.CurrentUserId)
x.CurrentMember = this;
},
x =>
{
x.RemoveMember(this);
if (x.Id == _client.CurrentUserId)
x.CurrentMember = null;
});
Status = UserStatus.Offline;
//_roles = new Dictionary<string, Role>();
_channels = new ConcurrentDictionary<string, Channel>();
_permissions = new ConcurrentDictionary<string, ChannelPermissions>();
_serverPermissions = new ServerPermissions();
}
internal override void OnCached()
{
if (_serverId != null)
{
var server = _client.Servers[_serverId];
server.AddMember(this);
if (Id == _client.CurrentUserId)
server.CurrentMember = this;
Server = server;
}
else
UpdateRoles(null);

var user = _client.GlobalUsers.GetOrAdd(Id);
user.AddUser(this);
GlobalUser = user;
if (serverId == null)
UpdateRoles(null);
}
internal override void OnUncached()
internal override void LoadReferences()
{
//References
var server = Server;
if (server != null)
{
server.RemoveMember(this);
if (Id == _client.CurrentUserId)
server.CurrentMember = null;
}

var globalUser = GlobalUser;
if (globalUser != null)
globalUser.RemoveUser(this);
_globalUser.Load();
_server.Load();
}
internal override void UnloadReferences()
{
_globalUser.Unload();
_server.Unload();
}

public override string ToString() => Id;
@@ -128,6 +126,7 @@ namespace Discord
{
if (model.User != null)
Update(model.User);

if (model.JoinedAt.HasValue)
JoinedAt = model.JoinedAt.Value;
if (model.Roles != null)
@@ -138,6 +137,7 @@ namespace Discord
internal void Update(ExtendedMemberInfo model)
{
Update(model as API.MemberInfo);

if (model.IsServerDeafened != null)
IsServerDeafened = model.IsServerDeafened.Value;
if (model.IsServerMuted != null)
@@ -156,9 +156,8 @@ namespace Discord
if (Status == UserStatus.Offline)
_lastOnline = DateTime.UtcNow;
}

//Allows null
GameId = model.GameId;
GameId = model.GameId; //Allows null
}
internal void Update(VoiceMemberInfo model)
{
@@ -188,7 +187,7 @@ namespace Discord
else
newRoles = new Dictionary<string, Role>();
Role everyone;
if (_serverId != null)
if (_server.Id != null)
everyone = Server.EveryoneRole;
else
everyone = _client.Roles.VirtualEveryone;


Loading…
Cancel
Save