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