@@ -3,9 +3,9 @@ using System; | |||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
namespace Discord.Data | |||||
namespace Discord | |||||
{ | { | ||||
public class DefaultDataStore : DataStore | |||||
public class DataStore | |||||
{ | { | ||||
private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2? | private const int CollectionConcurrencyLevel = 1; //WebSocket updater/event handler. //TODO: Needs profiling, increase to 2? | ||||
private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 | private const double AverageChannelsPerGuild = 10.22; //Source: Googie2149 | ||||
@@ -17,12 +17,12 @@ namespace Discord.Data | |||||
private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds; | private readonly ConcurrentDictionary<ulong, CachedGuild> _guilds; | ||||
private readonly ConcurrentDictionary<ulong, CachedGlobalUser> _users; | private readonly ConcurrentDictionary<ulong, CachedGlobalUser> _users; | ||||
internal override IReadOnlyCollection<ICachedChannel> Channels => _channels.ToReadOnlyCollection(); | |||||
internal override IReadOnlyCollection<CachedDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection(); | |||||
internal override IReadOnlyCollection<CachedGuild> Guilds => _guilds.ToReadOnlyCollection(); | |||||
internal override IReadOnlyCollection<CachedGlobalUser> Users => _users.ToReadOnlyCollection(); | |||||
internal IReadOnlyCollection<ICachedChannel> Channels => _channels.ToReadOnlyCollection(); | |||||
internal IReadOnlyCollection<CachedDMChannel> DMChannels => _dmChannels.ToReadOnlyCollection(); | |||||
internal IReadOnlyCollection<CachedGuild> Guilds => _guilds.ToReadOnlyCollection(); | |||||
internal IReadOnlyCollection<CachedGlobalUser> Users => _users.ToReadOnlyCollection(); | |||||
public DefaultDataStore(int guildCount, int dmChannelCount) | |||||
public DataStore(int guildCount, int dmChannelCount) | |||||
{ | { | ||||
double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | double estimatedChannelCount = guildCount * AverageChannelsPerGuild + dmChannelCount; | ||||
double estimatedUsersCount = guildCount * AverageUsersPerGuild; | double estimatedUsersCount = guildCount * AverageUsersPerGuild; | ||||
@@ -32,18 +32,18 @@ namespace Discord.Data | |||||
_users = new ConcurrentDictionary<ulong, CachedGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); | _users = new ConcurrentDictionary<ulong, CachedGlobalUser>(CollectionConcurrencyLevel, (int)(estimatedUsersCount * CollectionMultiplier)); | ||||
} | } | ||||
internal override ICachedChannel GetChannel(ulong id) | |||||
internal ICachedChannel GetChannel(ulong id) | |||||
{ | { | ||||
ICachedChannel channel; | ICachedChannel channel; | ||||
if (_channels.TryGetValue(id, out channel)) | if (_channels.TryGetValue(id, out channel)) | ||||
return channel; | return channel; | ||||
return null; | return null; | ||||
} | } | ||||
internal override void AddChannel(ICachedChannel channel) | |||||
internal void AddChannel(ICachedChannel channel) | |||||
{ | { | ||||
_channels[channel.Id] = channel; | _channels[channel.Id] = channel; | ||||
} | } | ||||
internal override ICachedChannel RemoveChannel(ulong id) | |||||
internal ICachedChannel RemoveChannel(ulong id) | |||||
{ | { | ||||
ICachedChannel channel; | ICachedChannel channel; | ||||
if (_channels.TryRemove(id, out channel)) | if (_channels.TryRemove(id, out channel)) | ||||
@@ -51,19 +51,19 @@ namespace Discord.Data | |||||
return null; | return null; | ||||
} | } | ||||
internal override CachedDMChannel GetDMChannel(ulong userId) | |||||
internal CachedDMChannel GetDMChannel(ulong userId) | |||||
{ | { | ||||
CachedDMChannel channel; | CachedDMChannel channel; | ||||
if (_dmChannels.TryGetValue(userId, out channel)) | if (_dmChannels.TryGetValue(userId, out channel)) | ||||
return channel; | return channel; | ||||
return null; | return null; | ||||
} | } | ||||
internal override void AddDMChannel(CachedDMChannel channel) | |||||
internal void AddDMChannel(CachedDMChannel channel) | |||||
{ | { | ||||
_channels[channel.Id] = channel; | _channels[channel.Id] = channel; | ||||
_dmChannels[channel.Recipient.Id] = channel; | _dmChannels[channel.Recipient.Id] = channel; | ||||
} | } | ||||
internal override CachedDMChannel RemoveDMChannel(ulong userId) | |||||
internal CachedDMChannel RemoveDMChannel(ulong userId) | |||||
{ | { | ||||
CachedDMChannel channel; | CachedDMChannel channel; | ||||
ICachedChannel ignored; | ICachedChannel ignored; | ||||
@@ -75,18 +75,18 @@ namespace Discord.Data | |||||
return null; | return null; | ||||
} | } | ||||
internal override CachedGuild GetGuild(ulong id) | |||||
internal CachedGuild GetGuild(ulong id) | |||||
{ | { | ||||
CachedGuild guild; | CachedGuild guild; | ||||
if (_guilds.TryGetValue(id, out guild)) | if (_guilds.TryGetValue(id, out guild)) | ||||
return guild; | return guild; | ||||
return null; | return null; | ||||
} | } | ||||
internal override void AddGuild(CachedGuild guild) | |||||
internal void AddGuild(CachedGuild guild) | |||||
{ | { | ||||
_guilds[guild.Id] = guild; | _guilds[guild.Id] = guild; | ||||
} | } | ||||
internal override CachedGuild RemoveGuild(ulong id) | |||||
internal CachedGuild RemoveGuild(ulong id) | |||||
{ | { | ||||
CachedGuild guild; | CachedGuild guild; | ||||
if (_guilds.TryRemove(id, out guild)) | if (_guilds.TryRemove(id, out guild)) | ||||
@@ -94,18 +94,18 @@ namespace Discord.Data | |||||
return null; | return null; | ||||
} | } | ||||
internal override CachedGlobalUser GetUser(ulong id) | |||||
internal CachedGlobalUser GetUser(ulong id) | |||||
{ | { | ||||
CachedGlobalUser user; | CachedGlobalUser user; | ||||
if (_users.TryGetValue(id, out user)) | if (_users.TryGetValue(id, out user)) | ||||
return user; | return user; | ||||
return null; | return null; | ||||
} | } | ||||
internal override CachedGlobalUser GetOrAddUser(ulong id, Func<ulong, CachedGlobalUser> userFactory) | |||||
internal CachedGlobalUser GetOrAddUser(ulong id, Func<ulong, CachedGlobalUser> userFactory) | |||||
{ | { | ||||
return _users.GetOrAdd(id, userFactory); | return _users.GetOrAdd(id, userFactory); | ||||
} | } | ||||
internal override CachedGlobalUser RemoveUser(ulong id) | |||||
internal CachedGlobalUser RemoveUser(ulong id) | |||||
{ | { | ||||
CachedGlobalUser user; | CachedGlobalUser user; | ||||
if (_users.TryRemove(id, out user)) | if (_users.TryRemove(id, out user)) |
@@ -1,4 +0,0 @@ | |||||
namespace Discord.Data | |||||
{ | |||||
public delegate DataStore DataStoreProvider(int shardId, int totalShards, int guildCount, int dmCount); | |||||
} |
@@ -1,29 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Discord.Data | |||||
{ | |||||
public abstract class DataStore | |||||
{ | |||||
internal abstract IReadOnlyCollection<ICachedChannel> Channels { get; } | |||||
internal abstract IReadOnlyCollection<CachedDMChannel> DMChannels { get; } | |||||
internal abstract IReadOnlyCollection<CachedGuild> Guilds { get; } | |||||
internal abstract IReadOnlyCollection<CachedGlobalUser> Users { get; } | |||||
internal abstract ICachedChannel GetChannel(ulong id); | |||||
internal abstract void AddChannel(ICachedChannel channel); | |||||
internal abstract ICachedChannel RemoveChannel(ulong id); | |||||
internal abstract CachedDMChannel GetDMChannel(ulong userId); | |||||
internal abstract void AddDMChannel(CachedDMChannel channel); | |||||
internal abstract CachedDMChannel RemoveDMChannel(ulong userId); | |||||
internal abstract CachedGuild GetGuild(ulong id); | |||||
internal abstract void AddGuild(CachedGuild guild); | |||||
internal abstract CachedGuild RemoveGuild(ulong id); | |||||
internal abstract CachedGlobalUser GetUser(ulong id); | |||||
internal abstract CachedGlobalUser GetOrAddUser(ulong userId, Func<ulong, CachedGlobalUser> userFactory); | |||||
internal abstract CachedGlobalUser RemoveUser(ulong id); | |||||
} | |||||
} |
@@ -1,11 +0,0 @@ | |||||
namespace Discord.Data | |||||
{ | |||||
//TODO: Implement | |||||
//TODO: CachedPublicUser's GuildCount system is not at all multi-writer threadsafe! | |||||
//TODO: CachedPublicUser's Update method is not multi-writer threadsafe! | |||||
//TODO: Are there other multiwriters across shards? | |||||
/*public class SharedDataStore | |||||
{ | |||||
}*/ | |||||
} |
@@ -1,5 +1,4 @@ | |||||
using Discord.API.Gateway; | using Discord.API.Gateway; | ||||
using Discord.Data; | |||||
using Discord.Extensions; | using Discord.Extensions; | ||||
using Discord.Logging; | using Discord.Logging; | ||||
using Discord.Net.Converters; | using Discord.Net.Converters; | ||||
@@ -26,7 +25,6 @@ namespace Discord | |||||
#if BENCHMARK | #if BENCHMARK | ||||
private readonly Logger _benchmarkLogger; | private readonly Logger _benchmarkLogger; | ||||
#endif | #endif | ||||
private readonly DataStoreProvider _dataStoreProvider; | |||||
private readonly JsonSerializer _serializer; | private readonly JsonSerializer _serializer; | ||||
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay; | private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay; | ||||
private readonly int _largeThreshold; | private readonly int _largeThreshold; | ||||
@@ -71,7 +69,6 @@ namespace Discord | |||||
_connectionTimeout = config.ConnectionTimeout; | _connectionTimeout = config.ConnectionTimeout; | ||||
_reconnectDelay = config.ReconnectDelay; | _reconnectDelay = config.ReconnectDelay; | ||||
_failedReconnectDelay = config.FailedReconnectDelay; | _failedReconnectDelay = config.FailedReconnectDelay; | ||||
_dataStoreProvider = config.DataStoreProvider; | |||||
MessageCacheSize = config.MessageCacheSize; | MessageCacheSize = config.MessageCacheSize; | ||||
_largeThreshold = config.LargeThreshold; | _largeThreshold = config.LargeThreshold; | ||||
@@ -481,7 +478,7 @@ namespace Discord | |||||
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); | ||||
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); | ||||
var dataStore = _dataStoreProvider(ShardId, _totalShards, data.Guilds.Length, data.PrivateChannels.Length); | |||||
var dataStore = new DataStore( data.Guilds.Length, data.PrivateChannels.Length); | |||||
var currentUser = new CachedSelfUser(this, data.User); | var currentUser = new CachedSelfUser(this, data.User); | ||||
int unavailableGuilds = 0; | int unavailableGuilds = 0; | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.Data; | |||||
using Discord.Net.WebSockets; | |||||
using Discord.Net.WebSockets; | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
@@ -29,11 +28,7 @@ namespace Discord | |||||
/// Decreasing this may reduce CPU usage while increasing login time and network usage. | /// Decreasing this may reduce CPU usage while increasing login time and network usage. | ||||
/// </summary> | /// </summary> | ||||
public int LargeThreshold { get; set; } = 250; | public int LargeThreshold { get; set; } = 250; | ||||
//Engines | |||||
/// <summary> Gets or sets the provider used to generate datastores. </summary> | |||||
public DataStoreProvider DataStoreProvider { get; set; } = (shardId, totalShards, guildCount, dmCount) => new DefaultDataStore(guildCount, dmCount); | |||||
/// <summary> Gets or sets the provider used to generate new websocket connections. </summary> | /// <summary> Gets or sets the provider used to generate new websocket connections. </summary> | ||||
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); | public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); | ||||
} | } | ||||
@@ -1,4 +1,5 @@ | |||||
using System; | using System; | ||||
using Discord.API; | |||||
using Model = Discord.API.User; | using Model = Discord.API.User; | ||||
namespace Discord | namespace Discord | ||||
@@ -33,6 +34,12 @@ namespace Discord | |||||
} | } | ||||
} | } | ||||
public override void Update(Model model, UpdateSource source) | |||||
{ | |||||
lock (this) | |||||
base.Update(model, source); | |||||
} | |||||
public CachedGlobalUser Clone() => MemberwiseClone() as CachedGlobalUser; | public CachedGlobalUser Clone() => MemberwiseClone() as CachedGlobalUser; | ||||
ICachedUser ICachedUser.Clone() => Clone(); | ICachedUser ICachedUser.Clone() => Clone(); | ||||
} | } | ||||
@@ -1,5 +1,4 @@ | |||||
using Discord.Data; | |||||
using Discord.Extensions; | |||||
using Discord.Extensions; | |||||
using System; | using System; | ||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||