Add new reference system
This commit is contained in:
@@ -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
src/Discord.Net/Helpers/Reference.cs
Normal file
68
src/Discord.Net/Helpers/Reference.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
if (serverId == null)
|
||||
UpdateRoles(null);
|
||||
|
||||
var user = _client.GlobalUsers.GetOrAdd(Id);
|
||||
user.AddUser(this);
|
||||
GlobalUser = user;
|
||||
}
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user