Fixed race condition, better handled serialization errors, and added singleton store
This commit is contained in:
@@ -94,9 +94,6 @@
|
||||
<Compile Include="..\Discord.Net\API\Enums\PermissionTarget.cs">
|
||||
<Link>API\Enums\PermissionTarget.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\API\Enums\Region.cs">
|
||||
<Link>API\Enums\Region.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\API\Enums\StringEnum.cs">
|
||||
<Link>API\Enums\StringEnum.cs</Link>
|
||||
</Compile>
|
||||
@@ -259,6 +256,9 @@
|
||||
<Compile Include="..\Discord.Net\Models\Permissions.cs">
|
||||
<Link>Models\Permissions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Region.cs">
|
||||
<Link>Models\Region.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Discord.Net\Models\Role.cs">
|
||||
<Link>Models\Role.cs</Link>
|
||||
</Compile>
|
||||
|
||||
11
src/Discord.Net/API/Converters/StringEnumConverter.cs
Normal file
11
src/Discord.Net/API/Converters/StringEnumConverter.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.API.Converters
|
||||
{
|
||||
public class StringEnumConverter
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ namespace Discord
|
||||
{
|
||||
public class DiscordAPIClientConfig
|
||||
{
|
||||
internal static readonly string UserAgent = $"Discord.Net/{DiscordClient.Version} (https://github.com/RogueException/Discord.Net)";
|
||||
|
||||
/// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary>
|
||||
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } }
|
||||
private LogMessageSeverity _logLevel = LogMessageSeverity.Info;
|
||||
@@ -21,15 +23,6 @@ namespace Discord
|
||||
public NetworkCredential ProxyCredentials { get { return _proxyCredentials; } set { SetValue(ref _proxyCredentials, value); } }
|
||||
private NetworkCredential _proxyCredentials = null;
|
||||
|
||||
internal string UserAgent
|
||||
{
|
||||
get
|
||||
{
|
||||
string version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(2);
|
||||
return $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)";
|
||||
}
|
||||
}
|
||||
|
||||
//Lock
|
||||
protected bool _isLocked;
|
||||
internal void Lock() { _isLocked = true; }
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace Discord
|
||||
}
|
||||
|
||||
/// <summary> Deserializes messages from JSON format and imports them into the message cache.</summary>
|
||||
public IEnumerable<Message> ImportMessages(string json)
|
||||
public IEnumerable<Message> ImportMessages(Channel channel, string json)
|
||||
{
|
||||
if (json == null) throw new ArgumentNullException(nameof(json));
|
||||
|
||||
@@ -269,8 +269,8 @@ namespace Discord
|
||||
.Select(x =>
|
||||
{
|
||||
var msg = new Message(this,
|
||||
x["Id"].Value<long>(),
|
||||
x["ChannelId"].Value<long>(),
|
||||
x["Id"].Value<long>(),
|
||||
channel.Id,
|
||||
x["UserId"].Value<long>());
|
||||
|
||||
var reader = x.CreateReader();
|
||||
|
||||
@@ -86,19 +86,19 @@ namespace Discord
|
||||
if (region == null) throw new ArgumentNullException(nameof(region));
|
||||
CheckReady();
|
||||
|
||||
var response = await _api.CreateServer(name, region.Value).ConfigureAwait(false);
|
||||
var response = await _api.CreateServer(name, region.Id).ConfigureAwait(false);
|
||||
var server = _servers.GetOrAdd(response.Id);
|
||||
server.Update(response);
|
||||
return server;
|
||||
}
|
||||
|
||||
/// <summary> Edits the provided server, changing only non-null attributes. </summary>
|
||||
public async Task EditServer(Server server, string name = null, Region region = null, ImageType iconType = ImageType.Png, byte[] icon = null)
|
||||
public async Task EditServer(Server server, string name = null, string region = null, ImageType iconType = ImageType.Png, byte[] icon = null)
|
||||
{
|
||||
if (server == null) throw new ArgumentNullException(nameof(server));
|
||||
CheckReady();
|
||||
|
||||
var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region.Value, iconType: iconType, icon: icon).ConfigureAwait(false);
|
||||
var response = await _api.EditServer(server.Id, name: name ?? server.Name, region: region, iconType: iconType, icon: icon).ConfigureAwait(false);
|
||||
server.Update(response);
|
||||
}
|
||||
|
||||
@@ -112,5 +112,12 @@ namespace Discord
|
||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||
//return _servers.TryRemove(server.Id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Region>> GetVoiceRegions()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
return (await _api.GetVoiceRegions()).Select(x => new Region { Id = x.Id, Name = x.Name, Hostname = x.Hostname, Port = x.Port });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
@@ -12,12 +13,14 @@ namespace Discord
|
||||
/// <summary> Provides a connection to the DiscordApp service. </summary>
|
||||
public sealed partial class DiscordClient : DiscordWSClient
|
||||
{
|
||||
public static readonly string Version = typeof(DiscordClientConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3);
|
||||
|
||||
private readonly DiscordAPIClient _api;
|
||||
private readonly Random _rand;
|
||||
private readonly JsonSerializer _socketSerializer, _messageImporter;
|
||||
private readonly JsonSerializer _messageImporter;
|
||||
private readonly ConcurrentQueue<Message> _pendingMessages;
|
||||
private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients;
|
||||
private readonly Dictionary<Type, IService> _services;
|
||||
private readonly Dictionary<Type, object> _singletons;
|
||||
private bool _sentInitialLog;
|
||||
private uint _nextVoiceClientId;
|
||||
private UserStatus _status;
|
||||
@@ -47,7 +50,7 @@ namespace Discord
|
||||
_roles = new Roles(this, cacheLock);
|
||||
_servers = new Servers(this, cacheLock);
|
||||
_globalUsers = new GlobalUsers(this, cacheLock);
|
||||
_services = new Dictionary<Type, IService>();
|
||||
_singletons = new Dictionary<Type, object>();
|
||||
|
||||
_status = UserStatus.Online;
|
||||
|
||||
@@ -162,16 +165,9 @@ namespace Discord
|
||||
|
||||
if (Config.UseMessageQueue)
|
||||
_pendingMessages = new ConcurrentQueue<Message>();
|
||||
|
||||
_socketSerializer = new JsonSerializer();
|
||||
_socketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
|
||||
#if TEST_RESPONSES
|
||||
_serializer.CheckAdditionalContent = true;
|
||||
_serializer.MissingMemberHandling = MissingMemberHandling.Error;
|
||||
#endif
|
||||
|
||||
|
||||
_messageImporter = new JsonSerializer();
|
||||
_messageImporter.ContractResolver = new MessageImporterResolver();
|
||||
_messageImporter.ContractResolver = new Message.ImportResolver();
|
||||
}
|
||||
internal override VoiceWebSocket CreateVoiceSocket()
|
||||
{
|
||||
@@ -276,25 +272,34 @@ namespace Discord
|
||||
_privateUser = null;
|
||||
}
|
||||
|
||||
public T AddSingleton<T>(T obj)
|
||||
where T : class
|
||||
{
|
||||
_singletons.Add(typeof(T), obj);
|
||||
return obj;
|
||||
}
|
||||
public T GetSingleton<T>(bool required = true)
|
||||
where T : class
|
||||
{
|
||||
object singleton;
|
||||
T singletonT = null;
|
||||
if (_singletons.TryGetValue(typeof(T), out singleton))
|
||||
singletonT = singleton as T;
|
||||
|
||||
if (singletonT == null && required)
|
||||
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}.");
|
||||
return singletonT;
|
||||
}
|
||||
public T AddService<T>(T obj)
|
||||
where T : class, IService
|
||||
{
|
||||
_services.Add(typeof(T), obj);
|
||||
AddSingleton(obj);
|
||||
obj.Install(this);
|
||||
return obj;
|
||||
}
|
||||
public T GetService<T>(bool required = true)
|
||||
where T : class, IService
|
||||
{
|
||||
IService service;
|
||||
T serviceT = null;
|
||||
if (_services.TryGetValue(typeof(T), out service))
|
||||
serviceT = service as T;
|
||||
|
||||
if (serviceT == null && required)
|
||||
throw new InvalidOperationException($"This operation requires {nameof(T)} to be added to {nameof(DiscordClient)}.");
|
||||
return serviceT;
|
||||
}
|
||||
=> GetSingleton<T>(required);
|
||||
|
||||
protected override IEnumerable<Task> GetTasks()
|
||||
{
|
||||
@@ -314,7 +319,7 @@ namespace Discord
|
||||
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>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<ReadyEvent>(_dataSocketSerializer);
|
||||
_privateUser = _users.GetOrAdd(data.User.Id, null);
|
||||
_privateUser.Update(data.User);
|
||||
_privateUser.Global.Update(data.User);
|
||||
@@ -339,7 +344,7 @@ namespace Discord
|
||||
//Servers
|
||||
case "GUILD_CREATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<GuildCreateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<GuildCreateEvent>(_dataSocketSerializer);
|
||||
if (data.Unavailable != true)
|
||||
{
|
||||
var server = _servers.GetOrAdd(data.Id);
|
||||
@@ -353,7 +358,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<GuildUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<GuildUpdateEvent>(_dataSocketSerializer);
|
||||
var server = _servers[data.Id];
|
||||
if (server != null)
|
||||
{
|
||||
@@ -364,7 +369,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_DELETE":
|
||||
{
|
||||
var data = e.Payload.ToObject<GuildDeleteEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<GuildDeleteEvent>(_dataSocketSerializer);
|
||||
var server = _servers.TryRemove(data.Id);
|
||||
if (server != null)
|
||||
{
|
||||
@@ -379,7 +384,7 @@ namespace Discord
|
||||
//Channels
|
||||
case "CHANNEL_CREATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<ChannelCreateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<ChannelCreateEvent>(_dataSocketSerializer);
|
||||
Channel channel;
|
||||
if (data.IsPrivate)
|
||||
{
|
||||
@@ -395,7 +400,7 @@ namespace Discord
|
||||
break;
|
||||
case "CHANNEL_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<ChannelUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<ChannelUpdateEvent>(_dataSocketSerializer);
|
||||
var channel = _channels[data.Id];
|
||||
if (channel != null)
|
||||
{
|
||||
@@ -406,7 +411,7 @@ namespace Discord
|
||||
break;
|
||||
case "CHANNEL_DELETE":
|
||||
{
|
||||
var data = e.Payload.ToObject<ChannelDeleteEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<ChannelDeleteEvent>(_dataSocketSerializer);
|
||||
var channel = _channels.TryRemove(data.Id);
|
||||
if (channel != null)
|
||||
RaiseChannelDestroyed(channel);
|
||||
@@ -416,7 +421,7 @@ namespace Discord
|
||||
//Members
|
||||
case "GUILD_MEMBER_ADD":
|
||||
{
|
||||
var data = e.Payload.ToObject<MemberAddEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MemberAddEvent>(_dataSocketSerializer);
|
||||
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
|
||||
user.Update(data);
|
||||
if (Config.TrackActivity)
|
||||
@@ -426,7 +431,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_MEMBER_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MemberUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MemberUpdateEvent>(_dataSocketSerializer);
|
||||
var user = _users[data.User.Id, data.GuildId];
|
||||
if (user != null)
|
||||
{
|
||||
@@ -437,7 +442,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_MEMBER_REMOVE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MemberRemoveEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MemberRemoveEvent>(_dataSocketSerializer);
|
||||
var user = _users.TryRemove(data.UserId, data.GuildId);
|
||||
if (user != null)
|
||||
RaiseUserLeft(user);
|
||||
@@ -445,7 +450,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_MEMBERS_CHUNK":
|
||||
{
|
||||
var data = e.Payload.ToObject<MembersChunkEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MembersChunkEvent>(_dataSocketSerializer);
|
||||
foreach (var memberData in data.Members)
|
||||
{
|
||||
var user = _users.GetOrAdd(memberData.User.Id, memberData.GuildId);
|
||||
@@ -458,7 +463,7 @@ namespace Discord
|
||||
//Roles
|
||||
case "GUILD_ROLE_CREATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<RoleCreateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<RoleCreateEvent>(_dataSocketSerializer);
|
||||
var role = _roles.GetOrAdd(data.Data.Id, data.GuildId);
|
||||
role.Update(data.Data);
|
||||
var server = _servers[data.GuildId];
|
||||
@@ -469,7 +474,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_ROLE_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<RoleUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<RoleUpdateEvent>(_dataSocketSerializer);
|
||||
var role = _roles[data.Data.Id];
|
||||
if (role != null)
|
||||
{
|
||||
@@ -480,7 +485,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_ROLE_DELETE":
|
||||
{
|
||||
var data = e.Payload.ToObject<RoleDeleteEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<RoleDeleteEvent>(_dataSocketSerializer);
|
||||
var role = _roles.TryRemove(data.RoleId);
|
||||
if (role != null)
|
||||
{
|
||||
@@ -495,7 +500,7 @@ namespace Discord
|
||||
//Bans
|
||||
case "GUILD_BAN_ADD":
|
||||
{
|
||||
var data = e.Payload.ToObject<BanAddEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<BanAddEvent>(_dataSocketSerializer);
|
||||
var server = _servers[data.GuildId];
|
||||
if (server != null)
|
||||
{
|
||||
@@ -507,7 +512,7 @@ namespace Discord
|
||||
break;
|
||||
case "GUILD_BAN_REMOVE":
|
||||
{
|
||||
var data = e.Payload.ToObject<BanRemoveEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<BanRemoveEvent>(_dataSocketSerializer);
|
||||
var server = _servers[data.GuildId];
|
||||
if (server != null)
|
||||
{
|
||||
@@ -521,7 +526,7 @@ namespace Discord
|
||||
//Messages
|
||||
case "MESSAGE_CREATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MessageCreateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MessageCreateEvent>(_dataSocketSerializer);
|
||||
Message msg = null;
|
||||
|
||||
bool isAuthor = data.Author.Id == _userId;
|
||||
@@ -548,7 +553,7 @@ namespace Discord
|
||||
break;
|
||||
case "MESSAGE_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MessageUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MessageUpdateEvent>(_dataSocketSerializer);
|
||||
var msg = _messages[data.Id];
|
||||
if (msg != null)
|
||||
{
|
||||
@@ -559,7 +564,7 @@ namespace Discord
|
||||
break;
|
||||
case "MESSAGE_DELETE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MessageDeleteEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MessageDeleteEvent>(_dataSocketSerializer);
|
||||
var msg = _messages.TryRemove(data.Id);
|
||||
if (msg != null)
|
||||
RaiseMessageDeleted(msg);
|
||||
@@ -567,7 +572,7 @@ namespace Discord
|
||||
break;
|
||||
case "MESSAGE_ACK":
|
||||
{
|
||||
var data = e.Payload.ToObject<MessageAckEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MessageAckEvent>(_dataSocketSerializer);
|
||||
var msg = GetMessage(data.MessageId);
|
||||
if (msg != null)
|
||||
RaiseMessageReadRemotely(msg);
|
||||
@@ -577,7 +582,7 @@ namespace Discord
|
||||
//Statuses
|
||||
case "PRESENCE_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<PresenceUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<PresenceUpdateEvent>(_dataSocketSerializer);
|
||||
var user = _users.GetOrAdd(data.User.Id, data.GuildId);
|
||||
if (user != null)
|
||||
{
|
||||
@@ -588,7 +593,7 @@ namespace Discord
|
||||
break;
|
||||
case "TYPING_START":
|
||||
{
|
||||
var data = e.Payload.ToObject<TypingStartEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<TypingStartEvent>(_dataSocketSerializer);
|
||||
var channel = _channels[data.ChannelId];
|
||||
if (channel != null)
|
||||
{
|
||||
@@ -614,7 +619,7 @@ namespace Discord
|
||||
//Voice
|
||||
case "VOICE_STATE_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<MemberVoiceStateUpdateEvent>(_dataSocketSerializer);
|
||||
var user = _users[data.UserId, data.GuildId];
|
||||
if (user != null)
|
||||
{
|
||||
@@ -633,7 +638,7 @@ namespace Discord
|
||||
//Settings
|
||||
case "USER_UPDATE":
|
||||
{
|
||||
var data = e.Payload.ToObject<UserUpdateEvent>(_socketSerializer);
|
||||
var data = e.Payload.ToObject<UserUpdateEvent>(_dataSocketSerializer);
|
||||
var user = _globalUsers[data.Id];
|
||||
if (user != null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Discord.Net;
|
||||
using Discord.Net.WebSockets;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -23,9 +24,9 @@ namespace Discord
|
||||
protected readonly DiscordWSClientConfig _config;
|
||||
protected readonly ManualResetEvent _disconnectedEvent;
|
||||
protected readonly ManualResetEventSlim _connectedEvent;
|
||||
protected ExceptionDispatchInfo _disconnectReason;
|
||||
internal readonly DataWebSocket _dataSocket;
|
||||
internal readonly VoiceWebSocket _voiceSocket;
|
||||
protected ExceptionDispatchInfo _disconnectReason;
|
||||
protected string _gateway, _token;
|
||||
protected long? _userId, _voiceServerId;
|
||||
private Task _runTask;
|
||||
@@ -44,6 +45,10 @@ namespace Discord
|
||||
private CancellationTokenSource _cancelTokenSource;
|
||||
protected CancellationToken _cancelToken;
|
||||
|
||||
internal JsonSerializer DataSocketSerializer => _dataSocketSerializer;
|
||||
internal JsonSerializer VoiceSocketSerializer => _voiceSocketSerializer;
|
||||
protected readonly JsonSerializer _dataSocketSerializer, _voiceSocketSerializer;
|
||||
|
||||
/// <summary> Initializes a new instance of the DiscordClient class. </summary>
|
||||
public DiscordWSClient(DiscordWSClientConfig config = null)
|
||||
{
|
||||
@@ -55,6 +60,32 @@ namespace Discord
|
||||
_disconnectedEvent = new ManualResetEvent(true);
|
||||
_connectedEvent = new ManualResetEventSlim(false);
|
||||
|
||||
_dataSocketSerializer = new JsonSerializer();
|
||||
_dataSocketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
|
||||
#if TEST_RESPONSES
|
||||
_dataSocketSerializer.CheckAdditionalContent = true;
|
||||
_dataSocketSerializer.MissingMemberHandling = MissingMemberHandling.Error;
|
||||
#else
|
||||
_dataSocketSerializer.Error += (s, e) =>
|
||||
{
|
||||
e.ErrorContext.Handled = true;
|
||||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.DataWebSocket, "Serialization Failed", e.ErrorContext.Error);
|
||||
};
|
||||
#endif
|
||||
|
||||
_voiceSocketSerializer = new JsonSerializer();
|
||||
_voiceSocketSerializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
|
||||
#if TEST_RESPONSES
|
||||
_voiceSocketSerializer.CheckAdditionalContent = true;
|
||||
_voiceSocketSerializer.MissingMemberHandling = MissingMemberHandling.Error;
|
||||
#else
|
||||
_voiceSocketSerializer.Error += (s, e) =>
|
||||
{
|
||||
e.ErrorContext.Handled = true;
|
||||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.VoiceWebSocket, "Serialization Failed", e.ErrorContext.Error);
|
||||
};
|
||||
#endif
|
||||
|
||||
_dataSocket = CreateDataSocket();
|
||||
if (_config.EnableVoice)
|
||||
_voiceSocket = CreateVoiceSocket();
|
||||
|
||||
@@ -84,15 +84,21 @@ namespace Discord
|
||||
|
||||
lock (_writerLock)
|
||||
{
|
||||
TValue newItem = createFunc();
|
||||
result = _dictionary.GetOrAdd(key, newItem);
|
||||
if (result == newItem)
|
||||
if (!_dictionary.ContainsKey(key))
|
||||
{
|
||||
result.Cache();
|
||||
RaiseItemCreated(result);
|
||||
result = createFunc();
|
||||
if (result.Cache())
|
||||
{
|
||||
_dictionary.TryAdd(key, result);
|
||||
RaiseItemCreated(result);
|
||||
}
|
||||
else
|
||||
result.Uncache();
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return _dictionary[key];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
protected void Import(IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
@@ -101,9 +107,13 @@ namespace Discord
|
||||
foreach (var pair in items)
|
||||
{
|
||||
var value = pair.Value;
|
||||
_dictionary.TryAdd(pair.Key, value);
|
||||
value.Cache();
|
||||
RaiseItemCreated(value);
|
||||
if (value.Cache())
|
||||
{
|
||||
_dictionary.TryAdd(pair.Key, value);
|
||||
RaiseItemCreated(value);
|
||||
}
|
||||
else
|
||||
value.Uncache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,14 @@ namespace Discord
|
||||
_client = client;
|
||||
}
|
||||
|
||||
internal void Cache()
|
||||
internal bool Cache()
|
||||
{
|
||||
LoadReferences();
|
||||
_isCached = true;
|
||||
if (LoadReferences())
|
||||
{
|
||||
_isCached = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
internal void Uncache()
|
||||
{
|
||||
@@ -44,7 +48,7 @@ namespace Discord
|
||||
_isCached = false;
|
||||
}
|
||||
}
|
||||
internal abstract void LoadReferences();
|
||||
internal abstract bool LoadReferences();
|
||||
internal abstract void UnloadReferences();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
|
||||
public T Load()
|
||||
public bool Load()
|
||||
{
|
||||
return Value; //Used for precaching
|
||||
return Value != null; //Used for precaching
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
|
||||
@@ -124,12 +124,12 @@ namespace Discord
|
||||
if (client.Config.MessageCacheLength > 0)
|
||||
_messages = new ConcurrentDictionary<long, Message>();
|
||||
}
|
||||
internal override void LoadReferences()
|
||||
internal override bool LoadReferences()
|
||||
{
|
||||
if (IsPrivate)
|
||||
_recipient.Load();
|
||||
return _recipient.Load();
|
||||
else
|
||||
_server.Load();
|
||||
return _server.Load();
|
||||
}
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Discord
|
||||
{
|
||||
_users = new ConcurrentDictionary<long, User>();
|
||||
}
|
||||
internal override void LoadReferences() { }
|
||||
internal override bool LoadReferences() { return true; }
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
//Don't need to clean _users - they're considered owned by server
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Discord
|
||||
{
|
||||
XkcdCode = xkcdPass;
|
||||
}
|
||||
internal override void LoadReferences() { }
|
||||
internal override bool LoadReferences() { return true; }
|
||||
internal override void UnloadReferences() { }
|
||||
|
||||
internal void Update(InviteReference model)
|
||||
|
||||
@@ -15,24 +15,25 @@ namespace Discord
|
||||
Queued,
|
||||
Failed
|
||||
}
|
||||
internal class MessageImporterResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
if (member is PropertyInfo)
|
||||
{
|
||||
if (!(member as PropertyInfo).CanWrite)
|
||||
return null;
|
||||
|
||||
property.Writable = true; //Handles private setters
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Message : CachedObject<long>
|
||||
{
|
||||
internal class ImportResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
if (member is PropertyInfo)
|
||||
{
|
||||
if (member.Name == nameof(ChannelId) || !(member as PropertyInfo).CanWrite)
|
||||
return null;
|
||||
|
||||
property.Writable = true; //Handles private setters
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Attachment : File
|
||||
{
|
||||
/// <summary> Unique identifier for this file. </summary>
|
||||
@@ -173,6 +174,8 @@ namespace Discord
|
||||
x =>
|
||||
{
|
||||
var channel = Channel;
|
||||
if (channel == null) return null;
|
||||
|
||||
if (!channel.IsPrivate)
|
||||
return _client.Users[x, channel.Server.Id];
|
||||
else
|
||||
@@ -181,10 +184,9 @@ namespace Discord
|
||||
Attachments = _initialAttachments;
|
||||
Embeds = _initialEmbeds;
|
||||
}
|
||||
internal override void LoadReferences()
|
||||
internal override bool LoadReferences()
|
||||
{
|
||||
_channel.Load();
|
||||
_user.Load();
|
||||
return _channel.Load() && _user.Load();
|
||||
}
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
|
||||
10
src/Discord.Net/Models/Region.cs
Normal file
10
src/Discord.Net/Models/Region.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Region
|
||||
{
|
||||
public string Hostname;
|
||||
public int Port;
|
||||
public string Id;
|
||||
public string Name;
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,9 @@ namespace Discord
|
||||
Color = new Color(0);
|
||||
Color.Lock();
|
||||
}
|
||||
internal override void LoadReferences()
|
||||
internal override bool LoadReferences()
|
||||
{
|
||||
_server.Load();
|
||||
return _server.Load();
|
||||
}
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
|
||||
@@ -103,9 +103,10 @@ namespace Discord
|
||||
//Local Cache
|
||||
_bans = new ConcurrentDictionary<long, bool>();
|
||||
}
|
||||
internal override void LoadReferences()
|
||||
internal override bool LoadReferences()
|
||||
{
|
||||
_afkChannel.Load();
|
||||
return true;
|
||||
}
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
|
||||
@@ -148,10 +148,10 @@ namespace Discord
|
||||
if (serverId == null)
|
||||
UpdateRoles(null);
|
||||
}
|
||||
internal override void LoadReferences()
|
||||
internal override bool LoadReferences()
|
||||
{
|
||||
_globalUser.Load();
|
||||
_server.Load();
|
||||
return _globalUser.Load() &&
|
||||
(IsPrivate || _server.Load());
|
||||
}
|
||||
internal override void UnloadReferences()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Discord.Net.Rest
|
||||
{
|
||||
PreAuthenticate = false,
|
||||
ReadWriteTimeout = _config.APITimeout,
|
||||
UserAgent = _config.UserAgent
|
||||
UserAgent = DiscordAPIClientConfig.UserAgent
|
||||
};
|
||||
if (_config.ProxyUrl != null)
|
||||
_client.Proxy = new WebProxy(_config.ProxyUrl, true, new string[0], _config.ProxyCredentials);
|
||||
|
||||
@@ -82,13 +82,13 @@ namespace Discord.Net.WebSockets
|
||||
JToken token = msg.Payload as JToken;
|
||||
if (msg.Type == "READY")
|
||||
{
|
||||
var payload = token.ToObject<ReadyEvent>();
|
||||
var payload = token.ToObject<ReadyEvent>(_client.DataSocketSerializer);
|
||||
_sessionId = payload.SessionId;
|
||||
_heartbeatInterval = payload.HeartbeatInterval;
|
||||
}
|
||||
else if (msg.Type == "RESUMED")
|
||||
{
|
||||
var payload = token.ToObject<ResumedEvent>();
|
||||
var payload = token.ToObject<ResumedEvent>(_client.DataSocketSerializer);
|
||||
_heartbeatInterval = payload.HeartbeatInterval;
|
||||
}
|
||||
RaiseReceivedEvent(msg.Type, token);
|
||||
@@ -98,7 +98,7 @@ namespace Discord.Net.WebSockets
|
||||
break;
|
||||
case 7: //Redirect
|
||||
{
|
||||
var payload = (msg.Payload as JToken).ToObject<RedirectEvent>();
|
||||
var payload = (msg.Payload as JToken).ToObject<RedirectEvent>(_client.DataSocketSerializer);
|
||||
if (payload.Url != null)
|
||||
{
|
||||
Host = payload.Url;
|
||||
|
||||
@@ -443,7 +443,7 @@ namespace Discord.Net.WebSockets
|
||||
{
|
||||
if (_state != (int)WebSocketState.Connected)
|
||||
{
|
||||
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>();
|
||||
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_client.VoiceSocketSerializer);
|
||||
_heartbeatInterval = payload.HeartbeatInterval;
|
||||
_ssrc = payload.SSRC;
|
||||
_endpoint = new IPEndPoint((await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault(), payload.Port);
|
||||
@@ -486,7 +486,7 @@ namespace Discord.Net.WebSockets
|
||||
break;
|
||||
case 4: //SESSION_DESCRIPTION
|
||||
{
|
||||
var payload = (msg.Payload as JToken).ToObject<JoinServerEvent>();
|
||||
var payload = (msg.Payload as JToken).ToObject<JoinServerEvent>(_client.VoiceSocketSerializer);
|
||||
_secretKey = payload.SecretKey;
|
||||
SendIsTalking(true);
|
||||
EndConnect();
|
||||
@@ -494,7 +494,7 @@ namespace Discord.Net.WebSockets
|
||||
break;
|
||||
case 5:
|
||||
{
|
||||
var payload = (msg.Payload as JToken).ToObject<IsTalkingEvent>();
|
||||
var payload = (msg.Payload as JToken).ToObject<IsTalkingEvent>(_client.VoiceSocketSerializer);
|
||||
RaiseIsSpeaking(payload.UserId, payload.IsSpeaking);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user