diff --git a/src/Discord.Net.Core/Format.cs b/src/Discord.Net.Core/Format.cs
index aa822f99e..91a614cf8 100644
--- a/src/Discord.Net.Core/Format.cs
+++ b/src/Discord.Net.Core/Format.cs
@@ -3,7 +3,7 @@
public static class Format
{
// Characters which need escaping
- private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`" };
+ private static string[] _sensitiveCharacters = { "\\", "*", "_", "~", "`" };
/// Returns a markdown-formatted string with bold formatting.
public static string Bold(string text) => $"**{text}**";
@@ -26,7 +26,7 @@
/// Sanitizes the string, safely escaping any Markdown sequences.
public static string Sanitize(string text)
{
- foreach (string unsafeChar in SensitiveCharacters)
+ foreach (string unsafeChar in _sensitiveCharacters)
text = text.Replace(unsafeChar, $"\\{unsafeChar}");
return text;
}
diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs
index ed12ff383..405b47274 100644
--- a/src/Discord.Net.Rest/BaseDiscordClient.cs
+++ b/src/Discord.Net.Rest/BaseDiscordClient.cs
@@ -22,23 +22,25 @@ namespace Discord.Rest
private readonly SemaphoreSlim _stateLock;
private bool _isFirstLogin, _isDisposed;
- internal API.DiscordRestApiClient ApiClient { get; }
internal LogManager LogManager { get; }
+ internal API.DiscordRestApiClient ApiClient { get; private set; }
public LoginState LoginState { get; private set; }
public ISelfUser CurrentUser { get; protected set; }
public TokenType TokenType => ApiClient.AuthTokenType;
/// Creates a new REST-only discord client.
- internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client)
+ internal BaseDiscordClient(DiscordRestConfig config)
{
- ApiClient = client;
LogManager = new LogManager(config.LogLevel);
LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
_stateLock = new SemaphoreSlim(1, 1);
_restLogger = LogManager.CreateLogger("Rest");
_isFirstLogin = config.DisplayInitialLog;
-
+ }
+ internal void SetApiClient(API.DiscordRestApiClient client)
+ {
+ ApiClient = client;
ApiClient.RequestQueue.RateLimitTriggered += async (id, info) =>
{
if (info == null)
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index 1fac66ec5..5fb5e00ac 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -1,10 +1,9 @@
#pragma warning disable CS1591
using Discord.API.Rest;
using Discord.Net;
-using Discord.Net.Converters;
using Discord.Net.Queue;
using Discord.Net.Rest;
-using Newtonsoft.Json;
+using Discord.Serialization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -27,9 +26,9 @@ namespace Discord.API
public event Func SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent> _sentRequestEvent = new AsyncEvent>();
-
- protected readonly JsonSerializer _serializer;
+
protected readonly SemaphoreSlim _stateLock;
+ protected readonly ScopedSerializer _serializer;
private readonly RestClientProvider _restClientProvider;
protected bool _isDisposed;
@@ -45,13 +44,12 @@ namespace Discord.API
internal IRestClient RestClient { get; private set; }
internal ulong? CurrentUserId { get; set;}
- public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry,
- JsonSerializer serializer = null)
+ public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, ScopedSerializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
{
_restClientProvider = restClientProvider;
UserAgent = userAgent;
+ _serializer = serializer;
DefaultRetryMode = defaultRetryMode;
- _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() };
RequestQueue = new RequestQueue();
_stateLock = new SemaphoreSlim(1, 1);
@@ -1159,16 +1157,14 @@ namespace Discord.API
protected string SerializeJson(object value)
{
var sb = new StringBuilder(256);
- using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
- using (JsonWriter writer = new JsonTextWriter(text))
- _serializer.Serialize(writer, value);
+ using (var writer = new StringWriter(sb, CultureInfo.InvariantCulture))
+ _serializer.ToJson(writer, value);
return sb.ToString();
}
protected T DeserializeJson(Stream jsonStream)
{
- using (TextReader text = new StreamReader(jsonStream))
- using (JsonReader reader = new JsonTextReader(text))
- return _serializer.Deserialize(reader);
+ using (var reader = new StreamReader(jsonStream))
+ return _serializer.FromJson(reader);
}
internal class BucketIds
diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs
index aa9937008..e71339eff 100644
--- a/src/Discord.Net.Rest/DiscordRestClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestClient.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using Discord.Serialization;
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Threading.Tasks;
@@ -7,15 +8,23 @@ namespace Discord.Rest
{
public class DiscordRestClient : BaseDiscordClient, IDiscordClient
{
+ private readonly ScopedSerializer _serializer;
private RestApplication _applicationInfo;
public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser;
public DiscordRestClient() : this(new DiscordRestConfig()) { }
- public DiscordRestClient(DiscordRestConfig config) : base(config, CreateApiClient(config)) { }
+ public DiscordRestClient(DiscordRestConfig config) : base(config)
+ {
+ _serializer = Serializer.CreateScope();
+ _serializer.Error += async ex =>
+ {
+ await _restLogger.WarningAsync("Serializer Error", ex);
+ };
+
+ SetApiClient(new API.DiscordRestApiClient(config.RestClientProvider, DiscordConfig.UserAgent, _serializer, config.DefaultRetryMode));
+ }
- private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config)
- => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent);
internal override void Dispose(bool disposing)
{
if (disposing)
diff --git a/src/Discord.Net.Rest/Net/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs
index a54107829..d9211722d 100644
--- a/src/Discord.Net.Rest/Net/DefaultRestClient.cs
+++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs
@@ -1,5 +1,4 @@
-using Newtonsoft.Json;
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -18,7 +17,6 @@ namespace Discord.Net.Rest
private readonly HttpClient _client;
private readonly string _baseUrl;
- private readonly JsonSerializer _errorDeserializer;
private CancellationToken _cancelToken;
private bool _isDisposed;
@@ -35,7 +33,6 @@ namespace Discord.Net.Rest
SetHeader("accept-encoding", "gzip, deflate");
_cancelToken = CancellationToken.None;
- _errorDeserializer = new JsonSerializer();
}
private void Dispose(bool disposing)
{
diff --git a/src/Discord.Net.Rest/Net/Converters/ArrayConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/ArrayConverter.cs
similarity index 97%
rename from src/Discord.Net.Rest/Net/Converters/ArrayConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/ArrayConverter.cs
index 3cededb7b..a6b09eb51 100644
--- a/src/Discord.Net.Rest/Net/Converters/ArrayConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/ArrayConverter.cs
@@ -2,7 +2,7 @@
using System;
using System.Collections.Generic;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class ArrayConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/DiscordContractResolver.cs
similarity index 99%
rename from src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/DiscordContractResolver.cs
index b465fbed2..4345f37e1 100644
--- a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/DiscordContractResolver.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class DiscordContractResolver : DefaultContractResolver
{
diff --git a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/ImageConverter.cs
similarity index 96%
rename from src/Discord.Net.Rest/Net/Converters/ImageConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/ImageConverter.cs
index f4d591d7e..aa3b1c167 100644
--- a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/ImageConverter.cs
@@ -2,7 +2,7 @@
using System;
using Model = Discord.API.Image;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class ImageConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/NullableConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/NullableConverter.cs
similarity index 97%
rename from src/Discord.Net.Rest/Net/Converters/NullableConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/NullableConverter.cs
index 0b149e725..4bab750fa 100644
--- a/src/Discord.Net.Rest/Net/Converters/NullableConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/NullableConverter.cs
@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class NullableConverter : JsonConverter
where T : struct
diff --git a/src/Discord.Net.Rest/Net/Converters/OptionalConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/OptionalConverter.cs
similarity index 96%
rename from src/Discord.Net.Rest/Net/Converters/OptionalConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/OptionalConverter.cs
index 18b2a9e1c..b3b75331f 100644
--- a/src/Discord.Net.Rest/Net/Converters/OptionalConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/OptionalConverter.cs
@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class OptionalConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/PermissionTargetConverter.cs
similarity index 96%
rename from src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/PermissionTargetConverter.cs
index 0ed566a84..4f01436a8 100644
--- a/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/PermissionTargetConverter.cs
@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class PermissionTargetConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/StringEntityConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/StringEntityConverter.cs
similarity index 94%
rename from src/Discord.Net.Rest/Net/Converters/StringEntityConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/StringEntityConverter.cs
index d7dd58d71..fe7bd9af5 100644
--- a/src/Discord.Net.Rest/Net/Converters/StringEntityConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/StringEntityConverter.cs
@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class StringEntityConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/UInt64Converter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64Converter.cs
similarity index 94%
rename from src/Discord.Net.Rest/Net/Converters/UInt64Converter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/UInt64Converter.cs
index 27cbe9290..2b5b1724a 100644
--- a/src/Discord.Net.Rest/Net/Converters/UInt64Converter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64Converter.cs
@@ -2,7 +2,7 @@
using System;
using System.Globalization;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class UInt64Converter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/UInt64EntityConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityConverter.cs
similarity index 95%
rename from src/Discord.Net.Rest/Net/Converters/UInt64EntityConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityConverter.cs
index b8d8f1057..6726d1344 100644
--- a/src/Discord.Net.Rest/Net/Converters/UInt64EntityConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityConverter.cs
@@ -2,7 +2,7 @@
using System;
using System.Globalization;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class UInt64EntityConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityOrIdConverter.cs
similarity index 96%
rename from src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityOrIdConverter.cs
index ae8cf2cb2..fb599e80f 100644
--- a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/UInt64EntityOrIdConverter.cs
@@ -2,7 +2,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class UInt64EntityOrIdConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Net/Converters/UserStatusConverter.cs b/src/Discord.Net.Rest/Serialization/JsonConverters/UserStatusConverter.cs
similarity index 97%
rename from src/Discord.Net.Rest/Net/Converters/UserStatusConverter.cs
rename to src/Discord.Net.Rest/Serialization/JsonConverters/UserStatusConverter.cs
index c0a287c16..77ecf53ba 100644
--- a/src/Discord.Net.Rest/Net/Converters/UserStatusConverter.cs
+++ b/src/Discord.Net.Rest/Serialization/JsonConverters/UserStatusConverter.cs
@@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
-namespace Discord.Net.Converters
+namespace Discord.Serialization.JsonConverters
{
internal class UserStatusConverter : JsonConverter
{
diff --git a/src/Discord.Net.Rest/Serialization/Serializer.cs b/src/Discord.Net.Rest/Serialization/Serializer.cs
new file mode 100644
index 000000000..bbcce8bc3
--- /dev/null
+++ b/src/Discord.Net.Rest/Serialization/Serializer.cs
@@ -0,0 +1,80 @@
+using Discord.Serialization.JsonConverters;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.Serialization
+{
+ internal class Serializer
+ {
+ public static ScopedSerializer Global { get; } = new ScopedSerializer();
+
+ public static T FromJson(Stream stream) => Global.FromJson(stream);
+ public static T FromJson(StreamReader reader) => Global.FromJson(reader);
+ public static T FromJson(JsonTextReader reader) => Global.FromJson(reader);
+ public static T FromJson(JToken token) => Global.FromJson(token);
+
+ public static void ToJson(Stream stream, T obj) => Global.ToJson(stream, obj);
+ public static void ToJson(StreamWriter writer, T obj) => Global.ToJson(writer, obj);
+ public static void ToJson(JsonTextWriter writer, T obj) => Global.ToJson(writer, obj);
+
+ public static ScopedSerializer CreateScope() => new ScopedSerializer();
+ }
+
+ internal class ScopedSerializer
+ {
+ private readonly JsonSerializer _serializer;
+
+ private readonly AsyncEvent> _errorEvent = new AsyncEvent>();
+ public event Func Error
+ {
+ add { _errorEvent.Add(value); }
+ remove { _errorEvent.Remove(value); }
+ }
+
+ internal ScopedSerializer()
+ {
+ _serializer = new JsonSerializer
+ {
+ DateFormatString = "yyyy-MM-ddTHH:mm:ssZ",
+ ContractResolver = new DiscordContractResolver()
+ };
+ _serializer.Error += (s, e) =>
+ {
+ _errorEvent.InvokeAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
+ e.ErrorContext.Handled = true;
+ };
+ }
+
+ public T FromJson(Stream stream)
+ {
+ using (var reader = new StreamReader(stream, Encoding.UTF8, false, 1024, true)) //1KB buffer
+ return FromJson(reader);
+ }
+ public T FromJson(TextReader reader)
+ {
+ using (var jsonReader = new JsonTextReader(reader) { CloseInput = false })
+ return FromJson(jsonReader);
+ }
+ public T FromJson(JsonTextReader reader)
+ => _serializer.Deserialize(reader);
+ public T FromJson(JToken token)
+ => token.ToObject(_serializer);
+
+ public void ToJson(Stream stream, T obj)
+ {
+ using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, false)) //1KB buffer
+ ToJson(writer, obj);
+ }
+ public void ToJson(TextWriter writer, T obj)
+ {
+ using (var jsonWriter = new JsonTextWriter(writer) { CloseOutput = false })
+ ToJson(jsonWriter, obj);
+ }
+ public void ToJson(JsonTextWriter writer, T obj)
+ => _serializer.Serialize(writer, obj);
+ }
+}
diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs
index 50d467054..019dbd635 100644
--- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs
+++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs
@@ -4,6 +4,7 @@ using Discord.Net.Queue;
using Discord.Net.Rest;
using Discord.Net.WebSockets;
using Discord.Rpc;
+using Discord.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -19,12 +20,13 @@ namespace Discord.API
{
internal class DiscordRpcApiClient : DiscordRestApiClient, IDisposable
{
- private abstract class RpcRequest
+ private interface IRpcRequest
{
- public abstract Task SetResultAsync(JToken data, JsonSerializer serializer);
- public abstract Task SetExceptionAsync(JToken data, JsonSerializer serializer);
+ Task SetResultAsync(JToken data);
+ Task SetExceptionAsync(JToken data);
}
- private class RpcRequest : RpcRequest
+
+ private class RpcRequest : IRpcRequest
{
public TaskCompletionSource Promise { get; set; }
@@ -37,13 +39,13 @@ namespace Discord.API
Promise.TrySetCanceled(); //Doesn't need to be async, we're already in a separate task
});
}
- public override Task SetResultAsync(JToken data, JsonSerializer serializer)
+ public Task SetResultAsync(JToken data)
{
- return Promise.TrySetResultAsync(data.ToObject(serializer));
+ return Promise.TrySetResultAsync(Serializer.FromJson(data));
}
- public override Task SetExceptionAsync(JToken data, JsonSerializer serializer)
+ public Task SetExceptionAsync(JToken data)
{
- var error = data.ToObject(serializer);
+ var error = Serializer.FromJson(data);
return Promise.TrySetExceptionAsync(new RpcException(error.Code, error.Message));
}
}
@@ -58,40 +60,46 @@ namespace Discord.API
public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>();
- private readonly ConcurrentDictionary _requests;
+ private readonly string _clientId;
+ private readonly ConcurrentDictionary _requests;
private readonly IWebSocketClient _webSocketClient;
private readonly SemaphoreSlim _connectionLock;
- private readonly string _clientId;
+ private readonly MemoryStream _decompressionStream;
+ private readonly StreamReader _decompressionReader;
private CancellationTokenSource _stateCancelToken;
private string _origin;
public ConnectionState ConnectionState { get; private set; }
public DiscordRpcApiClient(string clientId, string userAgent, string origin, RestClientProvider restClientProvider, WebSocketProvider webSocketProvider,
- RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null)
- : base(restClientProvider, userAgent, defaultRetryMode, serializer)
+ RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
+ : base(restClientProvider, userAgent, defaultRetryMode)
{
_connectionLock = new SemaphoreSlim(1, 1);
_clientId = clientId;
_origin = origin;
- _requests = new ConcurrentDictionary();
-
+ _requests = new ConcurrentDictionary();
+
+ _decompressionStream = new MemoryStream(10 * 1024); //10 KB
+ _decompressionReader = new StreamReader(_decompressionStream);
+
_webSocketClient = webSocketProvider();
//_webSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+)
_webSocketClient.SetHeader("origin", _origin);
_webSocketClient.BinaryMessage += async (data, index, count) =>
{
using (var compressed = new MemoryStream(data, index + 2, count - 2))
- using (var decompressed = new MemoryStream())
{
+ _decompressionStream.Position = 0;
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
- zlib.CopyTo(decompressed);
- decompressed.Position = 0;
- using (var reader = new StreamReader(decompressed))
- using (var jsonReader = new JsonTextReader(reader))
+ zlib.CopyTo(_decompressionStream);
+ _decompressionStream.SetLength(_decompressionStream.Position);
+
+ _decompressionStream.Position = 0;
+ var msg = _serializer.FromJson(_decompressionReader);
+ if (msg != null)
{
- var msg = _serializer.Deserialize(jsonReader);
await _receivedRpcEvent.InvokeAsync(msg.Cmd, msg.Event, msg.Data).ConfigureAwait(false);
if (msg.Nonce.IsSpecified && msg.Nonce.Value.HasValue)
ProcessMessage(msg);
@@ -101,12 +109,14 @@ namespace Discord.API
_webSocketClient.TextMessage += async text =>
{
using (var reader = new StringReader(text))
- using (var jsonReader = new JsonTextReader(reader))
{
- var msg = _serializer.Deserialize(jsonReader);
- await _receivedRpcEvent.InvokeAsync(msg.Cmd, msg.Event, msg.Data).ConfigureAwait(false);
- if (msg.Nonce.IsSpecified && msg.Nonce.Value.HasValue)
- ProcessMessage(msg);
+ var msg = _serializer.FromJson(reader);
+ if (msg != null)
+ {
+ await _receivedRpcEvent.InvokeAsync(msg.Cmd, msg.Event, msg.Data).ConfigureAwait(false);
+ if (msg.Nonce.IsSpecified && msg.Nonce.Value.HasValue)
+ ProcessMessage(msg);
+ }
}
};
_webSocketClient.Closed += async ex =>
@@ -219,7 +229,7 @@ namespace Discord.API
payload = new API.Rpc.RpcFrame { Cmd = cmd, Event = evt, Args = payload, Nonce = guid };
if (payload != null)
{
- var json = SerializeJson(payload);
+ string json = SerializeJson(payload);
bytes = Encoding.UTF8.GetBytes(json);
}
@@ -249,7 +259,7 @@ namespace Discord.API
{
ClientId = _clientId,
Scopes = scopes,
- RpcToken = rpcToken != null ? rpcToken : Optional.Create()
+ RpcToken = rpcToken ?? Optional.Create()
};
if (options.Timeout == null)
options.Timeout = 60000; //This requires manual input on the user's end, lets give them more time
@@ -378,15 +388,15 @@ namespace Discord.API
private bool ProcessMessage(API.Rpc.RpcFrame msg)
{
- if (_requests.TryGetValue(msg.Nonce.Value.Value, out RpcRequest requestTracker))
+ if (_requests.TryGetValue(msg.Nonce.Value.Value, out IRpcRequest requestTracker))
{
if (msg.Event.GetValueOrDefault("") == "ERROR")
{
- var _ = requestTracker.SetExceptionAsync(msg.Data.GetValueOrDefault() as JToken, _serializer);
+ var _ = requestTracker.SetExceptionAsync(msg.Data.GetValueOrDefault() as JToken);
}
else
{
- var _ = requestTracker.SetResultAsync(msg.Data.GetValueOrDefault() as JToken, _serializer);
+ var _ = requestTracker.SetResultAsync(msg.Data.GetValueOrDefault() as JToken);
}
return true;
}
diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs
index 9c77fc919..dba477808 100644
--- a/src/Discord.Net.Rpc/DiscordRpcClient.cs
+++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs
@@ -1,8 +1,6 @@
using Discord.API.Rpc;
using Discord.Logging;
-using Discord.Net.Converters;
using Discord.Rest;
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
@@ -10,22 +8,23 @@ using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
+using Discord.Serialization;
namespace Discord.Rpc
{
public partial class DiscordRpcClient : BaseDiscordClient, IDiscordClient
{
- private readonly JsonSerializer _serializer;
private readonly ConnectionManager _connection;
private readonly Logger _rpcLogger;
private readonly SemaphoreSlim _stateLock, _authorizeLock;
+ private readonly ScopedSerializer _serializer;
public ConnectionState ConnectionState { get; private set; }
public IReadOnlyCollection Scopes { get; private set; }
public DateTimeOffset TokenExpiresAt { get; private set; }
internal new API.DiscordRpcApiClient ApiClient => base.ApiClient as API.DiscordRpcApiClient;
- public new RestSelfUser CurrentUser { get { return base.CurrentUser as RestSelfUser; } private set { base.CurrentUser = value; } }
+ public new RestSelfUser CurrentUser { get => base.CurrentUser as RestSelfUser; private set => base.CurrentUser = value; }
public RestApplication ApplicationInfo { get; private set; }
/// Creates a new RPC discord client.
@@ -38,18 +37,18 @@ namespace Discord.Rpc
_stateLock = new SemaphoreSlim(1, 1);
_authorizeLock = new SemaphoreSlim(1, 1);
_rpcLogger = LogManager.CreateLogger("RPC");
+
+ _serializer = Serializer.CreateScope();
+ _serializer.Error += async ex =>
+ {
+ await _rpcLogger.WarningAsync("Serializer Error", ex);
+ };
+
_connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout,
OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x);
_connection.Connected += () => _connectedEvent.InvokeAsync();
_connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex);
- _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
- _serializer.Error += (s, e) =>
- {
- _rpcLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
- e.ErrorContext.Handled = true;
- };
-
ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.ReceivedRpcEvent += ProcessMessageAsync;
}
@@ -185,10 +184,12 @@ namespace Discord.Rpc
{
if (func == null) throw new NullReferenceException(nameof(func));
- var settings = new VoiceProperties();
- settings.Input = new VoiceDeviceProperties();
- settings.Output = new VoiceDeviceProperties();
- settings.Mode = new VoiceModeProperties();
+ var settings = new VoiceProperties
+ {
+ Input = new VoiceDeviceProperties(),
+ Output = new VoiceDeviceProperties(),
+ Mode = new VoiceModeProperties()
+ };
func(settings);
var model = new API.Rpc.VoiceSettings
@@ -294,9 +295,9 @@ namespace Discord.Rpc
case "READY":
{
await _rpcLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
- RequestOptions options = new RequestOptions
+ var options = new RequestOptions
{
//CancellationToken = _cancelToken //TODO: Implement
};
@@ -336,7 +337,7 @@ namespace Discord.Rpc
case "CHANNEL_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var channel = RpcChannelSummary.Create(data);
await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
@@ -347,7 +348,7 @@ namespace Discord.Rpc
case "GUILD_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var guild = RpcGuildSummary.Create(data);
await _guildCreatedEvent.InvokeAsync(guild).ConfigureAwait(false);
@@ -356,7 +357,7 @@ namespace Discord.Rpc
case "GUILD_STATUS":
{
await _rpcLogger.DebugAsync("Received Dispatch (GUILD_STATUS)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var guildStatus = RpcGuildStatus.Create(data);
await _guildStatusUpdatedEvent.InvokeAsync(guildStatus).ConfigureAwait(false);
@@ -367,7 +368,7 @@ namespace Discord.Rpc
case "VOICE_STATE_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var voiceState = RpcVoiceState.Create(this, data);
await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -376,7 +377,7 @@ namespace Discord.Rpc
case "VOICE_STATE_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var voiceState = RpcVoiceState.Create(this, data);
await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -385,7 +386,7 @@ namespace Discord.Rpc
case "VOICE_STATE_DELETE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var voiceState = RpcVoiceState.Create(this, data);
await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -395,7 +396,7 @@ namespace Discord.Rpc
case "SPEAKING_START":
{
await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_START)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
await _speakingStartedEvent.InvokeAsync(data.UserId).ConfigureAwait(false);
}
@@ -403,7 +404,7 @@ namespace Discord.Rpc
case "SPEAKING_STOP":
{
await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_STOP)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
await _speakingStoppedEvent.InvokeAsync(data.UserId).ConfigureAwait(false);
}
@@ -411,7 +412,7 @@ namespace Discord.Rpc
case "VOICE_SETTINGS_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_SETTINGS_UPDATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var settings = VoiceSettings.Create(data);
await _voiceSettingsUpdated.InvokeAsync(settings).ConfigureAwait(false);
@@ -422,7 +423,7 @@ namespace Discord.Rpc
case "MESSAGE_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var msg = RpcMessage.Create(this, data.ChannelId, data.Message);
await _messageReceivedEvent.InvokeAsync(msg).ConfigureAwait(false);
@@ -431,7 +432,7 @@ namespace Discord.Rpc
case "MESSAGE_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
var msg = RpcMessage.Create(this, data.ChannelId, data.Message);
await _messageUpdatedEvent.InvokeAsync(msg).ConfigureAwait(false);
@@ -440,7 +441,7 @@ namespace Discord.Rpc
case "MESSAGE_DELETE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
- var data = (payload.Value as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload.Value as JToken);
await _messageDeletedEvent.InvokeAsync(data.ChannelId, data.Message.Id).ConfigureAwait(false);
}
diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs
index 1ed5c3851..dc0b07373 100644
--- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs
+++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs
@@ -1,9 +1,7 @@
using Discord.API.Voice;
using Discord.Audio.Streams;
using Discord.Logging;
-using Discord.Net.Converters;
using Discord.WebSocket;
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
@@ -13,6 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.InteropServices;
+using Discord.Serialization;
namespace Discord.Audio
{
@@ -32,7 +31,7 @@ namespace Discord.Audio
}
private readonly Logger _audioLogger;
- private readonly JsonSerializer _serializer;
+ private readonly ScopedSerializer _serializer;
private readonly ConnectionManager _connection;
private readonly SemaphoreSlim _stateLock;
private readonly ConcurrentQueue _heartbeatTimes;
@@ -68,7 +67,13 @@ namespace Discord.Audio
ChannelId = channelId;
_audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}");
- ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider);
+ _serializer = Serializer.CreateScope();
+ _serializer.Error += async ex =>
+ {
+ await _audioLogger.WarningAsync("Serializer Error", ex);
+ };
+
+ ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider, _serializer);
ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false);
//ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false);
@@ -85,13 +90,6 @@ namespace Discord.Audio
_ssrcMap = new ConcurrentDictionary();
_streams = new ConcurrentDictionary();
_frameBuffers = new ConcurrentQueue();
-
- _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
- _serializer.Error += (s, e) =>
- {
- _audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
- e.ErrorContext.Handled = true;
- };
LatencyUpdated += async (old, val) => await _audioLogger.DebugAsync($"Latency = {val} ms").ConfigureAwait(false);
UdpLatencyUpdated += async (old, val) => await _audioLogger.DebugAsync($"UDP Latency = {val} ms").ConfigureAwait(false);
@@ -239,7 +237,7 @@ namespace Discord.Audio
case VoiceOpCode.Ready:
{
await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
_ssrc = data.SSRC;
@@ -255,7 +253,7 @@ namespace Discord.Audio
case VoiceOpCode.SessionDescription:
{
await _audioLogger.DebugAsync("Received SessionDescription").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.Mode != DiscordVoiceAPIClient.Mode)
throw new InvalidOperationException($"Discord selected an unexpected mode: {data.Mode}");
@@ -291,7 +289,7 @@ namespace Discord.Audio
{
await _audioLogger.DebugAsync("Received Speaking").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
_ssrcMap[data.Ssrc] = data.UserId; //TODO: Memory Leak: SSRCs are never cleaned up
await _speakingUpdatedEvent.InvokeAsync(data.UserId, data.Speaking);
diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
index ab2cb9266..9d199ed69 100644
--- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
+using Discord.Serialization;
namespace Discord.WebSocket
{
@@ -13,6 +14,8 @@ namespace Discord.WebSocket
{
private readonly DiscordSocketConfig _baseConfig;
private readonly SemaphoreSlim _connectionGroupLock;
+ private readonly ScopedSerializer _serializer;
+
private int[] _shardIds;
private Dictionary _shardIdsToIndex;
private DiscordSocketClient[] _shards;
@@ -25,7 +28,7 @@ namespace Discord.WebSocket
public Game? Game => _shards[0].Game;
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
- public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } }
+ public new SocketSelfUser CurrentUser { get => base.CurrentUser as SocketSelfUser; private set => base.CurrentUser = value; }
public IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount());
public IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(() => GetPrivateChannelCount());
public IReadOnlyCollection Shards => _shards;
@@ -34,13 +37,12 @@ namespace Discord.WebSocket
/// Creates a new REST/WebSocket discord client.
public DiscordShardedClient() : this(null, new DiscordSocketConfig()) { }
/// Creates a new REST/WebSocket discord client.
- public DiscordShardedClient(DiscordSocketConfig config) : this(null, config, CreateApiClient(config)) { }
+ public DiscordShardedClient(DiscordSocketConfig config) : this(null, config) { }
/// Creates a new REST/WebSocket discord client.
public DiscordShardedClient(int[] ids) : this(ids, new DiscordSocketConfig()) { }
/// Creates a new REST/WebSocket discord client.
- public DiscordShardedClient(int[] ids, DiscordSocketConfig config) : this(ids, config, CreateApiClient(config)) { }
- private DiscordShardedClient(int[] ids, DiscordSocketConfig config, API.DiscordSocketApiClient client)
- : base(config, client)
+ public DiscordShardedClient(int[] ids, DiscordSocketConfig config)
+ : base(config)
{
if (config.ShardId != null)
throw new ArgumentException($"{nameof(config.ShardId)} must not be set.");
@@ -52,6 +54,14 @@ namespace Discord.WebSocket
_baseConfig = config;
_connectionGroupLock = new SemaphoreSlim(1, 1);
+ _serializer = Serializer.CreateScope();
+ _serializer.Error += async ex =>
+ {
+ await _restLogger.WarningAsync("Serializer Error", ex);
+ };
+
+ SetApiClient(new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordConfig.UserAgent, _serializer));
+
if (config.TotalShards == null)
_automaticShards = true;
else
@@ -69,8 +79,6 @@ namespace Discord.WebSocket
}
}
}
- private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
- => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent);
internal override async Task OnLoginAsync(TokenType tokenType, string token)
{
diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
index aff3f30a3..5355324ea 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
@@ -4,8 +4,8 @@ using Discord.API.Rest;
using Discord.Net.Queue;
using Discord.Net.Rest;
using Discord.Net.WebSockets;
+using Discord.Serialization;
using Discord.WebSocket;
-using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
@@ -36,13 +36,14 @@ namespace Discord.API
public ConnectionState ConnectionState { get; private set; }
- public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent,
- string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null)
- : base(restClientProvider, userAgent, defaultRetryMode, serializer)
+ public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, ScopedSerializer serializer,
+ string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
+ : base(restClientProvider, userAgent, serializer, defaultRetryMode)
{
_gatewayUrl = url;
if (url != null)
_isExplicitUrl = true;
+
_decompressionStream = new MemoryStream(10 * 1024); //10 KB
_decompressionReader = new StreamReader(_decompressionStream);
@@ -58,20 +59,16 @@ namespace Discord.API
_decompressionStream.SetLength(_decompressionStream.Position);
_decompressionStream.Position = 0;
- using (var jsonReader = new JsonTextReader(_decompressionReader) { CloseInput = false })
- {
- var msg = _serializer.Deserialize(jsonReader);
- if (msg != null)
- await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
- }
+ var msg = _serializer.FromJson(_decompressionReader);
+ if (msg != null)
+ await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
}
};
WebSocketClient.TextMessage += async text =>
{
using (var reader = new StringReader(text))
- using (var jsonReader = new JsonTextReader(reader))
{
- var msg = _serializer.Deserialize(jsonReader);
+ var msg = _serializer.FromJson(reader);
if (msg != null)
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
}
diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
index b13ceca1d..11b21b9df 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs
@@ -1,11 +1,10 @@
using Discord.API;
using Discord.API.Gateway;
using Discord.Logging;
-using Discord.Net.Converters;
using Discord.Net.Udp;
using Discord.Net.WebSockets;
using Discord.Rest;
-using Newtonsoft.Json;
+using Discord.Serialization;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
@@ -22,13 +21,12 @@ namespace Discord.WebSocket
public partial class DiscordSocketClient : BaseDiscordClient, IDiscordClient
{
private readonly ConcurrentQueue _largeGuilds;
- private readonly JsonSerializer _serializer;
- private readonly SemaphoreSlim _connectionGroupLock;
+ private readonly SemaphoreSlim _connectionGroupLock, _stateLock;
+ private readonly ScopedSerializer _serializer;
private readonly DiscordSocketClient _parentClient;
private readonly ConcurrentQueue _heartbeatTimes;
private readonly ConnectionManager _connection;
private readonly Logger _gatewayLogger;
- private readonly SemaphoreSlim _stateLock;
private string _sessionId;
private int _lastSeq;
@@ -72,10 +70,9 @@ namespace Discord.WebSocket
/// Creates a new REST/WebSocket discord client.
public DiscordSocketClient() : this(new DiscordSocketConfig()) { }
/// Creates a new REST/WebSocket discord client.
- public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) { }
- internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock, DiscordSocketClient parentClient) : this(config, CreateApiClient(config), groupLock, parentClient) { }
- private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient)
- : base(config, client)
+ public DiscordSocketClient(DiscordSocketConfig config) : this(config, null, null) { }
+ internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock, DiscordSocketClient parentClient)
+ : base(config)
{
ShardId = config.ShardId ?? 0;
TotalShards = config.TotalShards ?? 1;
@@ -87,9 +84,17 @@ namespace Discord.WebSocket
HandlerTimeout = config.HandlerTimeout;
State = new ClientState(0, 0);
_heartbeatTimes = new ConcurrentQueue();
-
_stateLock = new SemaphoreSlim(1, 1);
_gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}");
+
+ _serializer = Serializer.CreateScope();
+ _serializer.Error += async ex =>
+ {
+ await _restLogger.WarningAsync("Serializer Error", ex);
+ };
+
+ SetApiClient(new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordConfig.UserAgent, _serializer, config.GatewayHost, config.DefaultRetryMode));
+
_connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout,
OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x);
_connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected));
@@ -98,13 +103,6 @@ namespace Discord.WebSocket
_nextAudioId = 1;
_connectionGroupLock = groupLock;
_parentClient = parentClient;
-
- _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
- _serializer.Error += (s, e) =>
- {
- _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult();
- e.ErrorContext.Handled = true;
- };
ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.ReceivedGatewayEvent += ProcessMessageAsync;
@@ -127,8 +125,6 @@ namespace Discord.WebSocket
_voiceRegions = ImmutableDictionary.Create();
_largeGuilds = new ConcurrentQueue();
}
- private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config)
- => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost);
internal override void Dispose(bool disposing)
{
if (disposing)
@@ -399,7 +395,7 @@ namespace Discord.WebSocket
case GatewayOpCode.Hello:
{
await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
_heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken);
}
@@ -455,7 +451,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);
var currentUser = SocketSelfUser.Create(this, state, data.User);
@@ -525,7 +521,7 @@ namespace Discord.WebSocket
//Guilds
case "GUILD_CREATE":
{
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.Unavailable == false)
{
@@ -577,7 +573,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.Id);
if (guild != null)
{
@@ -596,7 +592,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -614,7 +610,7 @@ namespace Discord.WebSocket
case "GUILD_SYNC":
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.Id);
if (guild != null)
{
@@ -635,7 +631,7 @@ namespace Discord.WebSocket
break;
case "GUILD_DELETE":
{
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.Unavailable == true)
{
type = "GUILD_UNAVAILABLE";
@@ -677,7 +673,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
SocketChannel channel = null;
if (data.GuildId.IsSpecified)
{
@@ -709,7 +705,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var channel = State.GetChannel(data.Id);
if (channel != null)
{
@@ -737,7 +733,7 @@ namespace Discord.WebSocket
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false);
SocketChannel channel = null;
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.GuildId.IsSpecified)
{
var guild = State.GetGuild(data.GuildId.Value);
@@ -775,7 +771,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_ADD)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -801,7 +797,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -839,7 +835,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_REMOVE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -874,7 +870,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBERS_CHUNK)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -898,7 +894,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel)
{
var user = channel.GetOrAddUser(data.User);
@@ -915,7 +911,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel)
{
var user = channel.RemoveUser(data.User.Id);
@@ -940,7 +936,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_CREATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -964,7 +960,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -999,7 +995,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_DELETE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -1033,7 +1029,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_ADD)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -1059,7 +1055,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_REMOVE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -1087,7 +1083,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
@@ -1134,7 +1130,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
@@ -1181,7 +1177,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
@@ -1208,7 +1204,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
@@ -1232,7 +1228,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
@@ -1256,7 +1252,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage;
@@ -1278,7 +1274,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
@@ -1309,7 +1305,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.GuildId.IsSpecified)
{
@@ -1368,7 +1364,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
@@ -1390,7 +1386,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (USER_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
if (data.Id == CurrentUser.Id)
{
var before = CurrentUser.Clone();
@@ -1410,7 +1406,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
SocketUser user;
SocketVoiceState before, after;
if (data.GuildId != null)
@@ -1482,7 +1478,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
- var data = (payload as JToken).ToObject(_serializer);
+ var data = _serializer.FromJson(payload as JToken);
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
index 764bf5807..7817ef81c 100644
--- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
@@ -1,9 +1,9 @@
#pragma warning disable CS1591
using Discord.API;
using Discord.API.Voice;
-using Discord.Net.Converters;
using Discord.Net.Udp;
using Discord.Net.WebSockets;
+using Discord.Serialization;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
@@ -37,7 +37,7 @@ namespace Discord.Audio
public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>();
- private readonly JsonSerializer _serializer;
+ private readonly ScopedSerializer _serializer;
private readonly SemaphoreSlim _connectionLock;
private readonly MemoryStream _decompressionStream;
private readonly StreamReader _decompressionReader;
@@ -52,12 +52,14 @@ namespace Discord.Audio
public ushort UdpPort => _udp.Port;
- internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, UdpSocketProvider udpSocketProvider, JsonSerializer serializer = null)
+ internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, UdpSocketProvider udpSocketProvider, ScopedSerializer serializer)
{
GuildId = guildId;
_connectionLock = new SemaphoreSlim(1, 1);
_udp = udpSocketProvider();
_udp.ReceivedDatagram += (data, index, count) => _receivedPacketEvent.InvokeAsync(data, index, count);
+ _serializer = serializer;
+
_decompressionStream = new MemoryStream(10 * 1024); //10 KB
_decompressionReader = new StreamReader(_decompressionStream);
@@ -70,23 +72,19 @@ namespace Discord.Audio
_decompressionStream.Position = 0;
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
zlib.CopyTo(_decompressionStream);
+ _decompressionStream.SetLength(_decompressionStream.Position);
_decompressionStream.Position = 0;
- using (var jsonReader = new JsonTextReader(_decompressionReader) { CloseInput = false })
- {
- var msg = _serializer.Deserialize(jsonReader);
- if (msg != null)
- await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
- }
- _decompressionStream.SetLength(0);
+ var msg = _serializer.FromJson(_decompressionReader);
+ if (msg != null)
+ await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
}
};
WebSocketClient.TextMessage += async text =>
{
using (var reader = new StringReader(text))
- using (var jsonReader = new JsonTextReader(reader))
{
- var msg = _serializer.Deserialize(jsonReader);
+ var msg = _serializer.FromJson(reader);
if (msg != null)
await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false);
}
@@ -96,8 +94,6 @@ namespace Discord.Audio
await DisconnectAsync().ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
};
-
- _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
}
private void Dispose(bool disposing)
{
@@ -259,16 +255,14 @@ namespace Discord.Audio
private string SerializeJson(object value)
{
var sb = new StringBuilder(256);
- using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
- using (JsonWriter writer = new JsonTextWriter(text))
- _serializer.Serialize(writer, value);
+ using (TextWriter writer = new StringWriter(sb, CultureInfo.InvariantCulture))
+ _serializer.ToJson(writer, value);
return sb.ToString();
}
private T DeserializeJson(Stream jsonStream)
{
- using (TextReader text = new StreamReader(jsonStream))
- using (JsonReader reader = new JsonTextReader(text))
- return _serializer.Deserialize(reader);
+ using (TextReader reader = new StreamReader(jsonStream))
+ return _serializer.FromJson(reader);
}
}
}