diff --git a/README.md b/README.md index 268a6d81d..b287d7015 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Bleeding edge builds are available using our MyGet feed (`https://www.myget.org/ In order to compile Discord.Net, you require the following: ### Using Visual Studio -- [Visual Studio 2017 RC](https://www.microsoft.com/net/core#windowsvs2017) +- [Visual Studio 2017 RC Build 26014.0](https://www.microsoft.com/net/core#windowsvs2017) The .NET Core and Docker (Preview) workload is required during Visual Studio installation. diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index a501dfb66..776b799c9 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -28,8 +28,8 @@ namespace Discord.Commands public static Dictionary Build(CommandService service, params TypeInfo[] validTypes) => Build(validTypes, service); public static Dictionary Build(IEnumerable validTypes, CommandService service) { - if (!validTypes.Any()) - throw new InvalidOperationException("Could not find any valid modules from the given selection"); + /*if (!validTypes.Any()) + throw new InvalidOperationException("Could not find any valid modules from the given selection");*/ var topLevelGroups = validTypes.Where(x => x.DeclaringType == null); var subGroups = validTypes.Intersect(topLevelGroups); @@ -65,7 +65,8 @@ namespace Discord.Commands if (builtTypes.Contains(typeInfo)) continue; - builder.AddModule((module) => { + builder.AddModule((module) => + { BuildModule(module, typeInfo, service); BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service); }); @@ -106,7 +107,8 @@ namespace Discord.Commands foreach (var method in validCommands) { - builder.AddCommand((command) => { + builder.AddCommand((command) => + { BuildCommand(command, typeInfo, method, service); }); } @@ -147,21 +149,24 @@ namespace Discord.Commands int pos = 0, count = parameters.Length; foreach (var paramInfo in parameters) { - builder.AddParameter((parameter) => { + builder.AddParameter((parameter) => + { BuildParameter(parameter, paramInfo, pos++, count, service); }); } var createInstance = ReflectionUtils.CreateBuilder(typeInfo, service); - builder.Callback = (ctx, args, map) => { + builder.Callback = (ctx, args, map) => + { var instance = createInstance(map); instance.Context = ctx; try { - return method.Invoke(instance, args) as Task ?? Task.CompletedTask; + return method.Invoke(instance, args) as Task ?? Task.Delay(0); } - finally{ + finally + { (instance as IDisposable)?.Dispose(); } }; diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 63ffafd2c..9e993b624 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,41 +1,25 @@ - - + A Discord.Net extension adding support for bot commands. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - - 1.0.0-alpha-20161104-2 - All - - - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Commands/project.json b/src/Discord.Net.Commands/project.json index a87591c03..d918d0e7e 100644 --- a/src/Discord.Net.Commands/project.json +++ b/src/Discord.Net.Commands/project.json @@ -32,12 +32,7 @@ }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } \ No newline at end of file diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index bc7436cd4..0d0b7914b 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -160,8 +160,8 @@ namespace Discord.API LoginState = LoginState.LoggedOut; } - internal virtual Task ConnectInternalAsync() => Task.CompletedTask; - internal virtual Task DisconnectInternalAsync() => Task.CompletedTask; + internal virtual Task ConnectInternalAsync() => Task.Delay(0); + internal virtual Task DisconnectInternalAsync() => Task.Delay(0); //Core internal Task SendAsync(string method, Expression> endpointExpr, BucketIds ids, diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index b2d9fc870..c64ed88c2 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,60 +1,30 @@ - - + A .Net API wrapper and bot framework for Discord. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 9.0.1 - - - 4.3.0 - - - 1.3.0 - - - 3.1.0 - - - 4.3.0 - - - 4.3.0 - All - + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 41bc79511..389a2cc27 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -9,8 +9,10 @@ namespace Discord { /// Sends a message to this message channel. Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Extensions/CollectionExtensions.cs b/src/Discord.Net.Core/Extensions/CollectionExtensions.cs index 8eebac817..e5d6025c2 100644 --- a/src/Discord.Net.Core/Extensions/CollectionExtensions.cs +++ b/src/Discord.Net.Core/Extensions/CollectionExtensions.cs @@ -8,24 +8,30 @@ namespace Discord { internal static class CollectionExtensions { - public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyDictionary source) - => new ConcurrentDictionaryWrapper(source.Select(x => x.Value), () => source.Count); + //public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyCollection source) + // => new CollectionWrapper(source, () => source.Count); + public static IReadOnlyCollection ToReadOnlyCollection(this ICollection source) + => new CollectionWrapper(source, () => source.Count); + //public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyDictionary source) + // => new CollectionWrapper(source.Select(x => x.Value), () => source.Count); + public static IReadOnlyCollection ToReadOnlyCollection(this IDictionary source) + => new CollectionWrapper(source.Select(x => x.Value), () => source.Count); public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable query, IReadOnlyCollection source) - => new ConcurrentDictionaryWrapper(query, () => source.Count); + => new CollectionWrapper(query, () => source.Count); public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable query, Func countFunc) - => new ConcurrentDictionaryWrapper(query, countFunc); + => new CollectionWrapper(query, countFunc); } [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal struct ConcurrentDictionaryWrapper : IReadOnlyCollection + internal struct CollectionWrapper : IReadOnlyCollection { private readonly IEnumerable _query; private readonly Func _countFunc; //It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected public int Count => _countFunc(); - - public ConcurrentDictionaryWrapper(IEnumerable query, Func countFunc) + + public CollectionWrapper(IEnumerable query, Func countFunc) { _query = query; _countFunc = countFunc; diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 104e02835..0a2fce738 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -17,56 +17,68 @@ namespace Discord.Logging ClientLogger = new Logger(this, "Discord"); } + public async Task LogAsync(LogSeverity severity, string source, Exception ex) + { + if (severity <= Level) + await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); + } public async Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null) { if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false); } +#if NETSTANDARD1_3 public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) { if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false); } - public async Task LogAsync(LogSeverity severity, string source, Exception ex) - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); - } +#endif + public Task ErrorAsync(string source, Exception ex) + => LogAsync(LogSeverity.Error, source, ex); public Task ErrorAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); +#if NETSTANDARD1_3 public Task ErrorAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); - public Task ErrorAsync(string source, Exception ex) - => LogAsync(LogSeverity.Error, source, ex); +#endif + public Task WarningAsync(string source, Exception ex) + => LogAsync(LogSeverity.Warning, source, ex); public Task WarningAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); +#if NETSTANDARD1_3 public Task WarningAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); - public Task WarningAsync(string source, Exception ex) - => LogAsync(LogSeverity.Warning, source, ex); +#endif + public Task InfoAsync(string source, Exception ex) + => LogAsync(LogSeverity.Info, source, ex); public Task InfoAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); +#if NETSTANDARD1_3 public Task InfoAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); - public Task InfoAsync(string source, Exception ex) - => LogAsync(LogSeverity.Info, source, ex); +#endif + public Task VerboseAsync(string source, Exception ex) + => LogAsync(LogSeverity.Verbose, source, ex); public Task VerboseAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); +#if NETSTANDARD1_3 public Task VerboseAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); - public Task VerboseAsync(string source, Exception ex) - => LogAsync(LogSeverity.Verbose, source, ex); +#endif + public Task DebugAsync(string source, Exception ex) + => LogAsync(LogSeverity.Debug, source, ex); public Task DebugAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); +#if NETSTANDARD1_3 public Task DebugAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); - public Task DebugAsync(string source, Exception ex) - => LogAsync(LogSeverity.Debug, source, ex); +#endif public Logger CreateLogger(string name) => new Logger(this, name); diff --git a/src/Discord.Net.Core/Logging/Logger.cs b/src/Discord.Net.Core/Logging/Logger.cs index c871c0b26..cff69a84c 100644 --- a/src/Discord.Net.Core/Logging/Logger.cs +++ b/src/Discord.Net.Core/Logging/Logger.cs @@ -20,42 +20,54 @@ namespace Discord.Logging => _manager.LogAsync(severity, Name, exception); public Task LogAsync(LogSeverity severity, string message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); +#if NETSTANDARD1_3 public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); +#endif + public Task ErrorAsync(Exception exception) + => _manager.ErrorAsync(Name, exception); public Task ErrorAsync(string message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task ErrorAsync(FormattableString message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); - public Task ErrorAsync(Exception exception) - => _manager.ErrorAsync(Name, exception); +#endif + public Task WarningAsync(Exception exception) + => _manager.WarningAsync(Name, exception); public Task WarningAsync(string message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task WarningAsync(FormattableString message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); - public Task WarningAsync(Exception exception) - => _manager.WarningAsync(Name, exception); +#endif + public Task InfoAsync(Exception exception) + => _manager.InfoAsync(Name, exception); public Task InfoAsync(string message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task InfoAsync(FormattableString message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); - public Task InfoAsync(Exception exception) - => _manager.InfoAsync(Name, exception); +#endif + public Task VerboseAsync(Exception exception) + => _manager.VerboseAsync(Name, exception); public Task VerboseAsync(string message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task VerboseAsync(FormattableString message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); - public Task VerboseAsync(Exception exception) - => _manager.VerboseAsync(Name, exception); +#endif + public Task DebugAsync(Exception exception) + => _manager.DebugAsync(Name, exception); public Task DebugAsync(string message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task DebugAsync(FormattableString message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); - public Task DebugAsync(Exception exception) - => _manager.DebugAsync(Name, exception); +#endif } } diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs index 78a8e63cb..8ee52171c 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs @@ -209,7 +209,7 @@ namespace Discord.Net.Queue #endif } - var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); DateTimeOffset? resetTick = null; //Using X-RateLimit-Remaining causes a race condition diff --git a/src/Discord.Net.Core/Net/RateLimitInfo.cs b/src/Discord.Net.Core/Net/RateLimitInfo.cs index 2c2faccf8..3c2b2acd5 100644 --- a/src/Discord.Net.Core/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Core/Net/RateLimitInfo.cs @@ -17,7 +17,7 @@ namespace Discord.Net IsGlobal = headers.TryGetValue("X-RateLimit-Global", out temp) ? bool.Parse(temp) : false; Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) ? int.Parse(temp) : (int?)null; Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) ? int.Parse(temp) : (int?)null; - Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeOffset.FromUnixTimeSeconds(int.Parse(temp)) : (DateTimeOffset?)null; + Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) ? int.Parse(temp) : (int?)null; } } diff --git a/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs b/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs new file mode 100644 index 000000000..8da948d1a --- /dev/null +++ b/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Udp +{ + public interface IUdpSocket + { + event Func ReceivedDatagram; + + void SetCancelToken(CancellationToken cancelToken); + void SetDestination(string host, int port); + + Task StartAsync(); + Task StopAsync(); + + Task SendAsync(byte[] data, int index, int count); + } +} diff --git a/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs b/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs new file mode 100644 index 000000000..07fbd4f57 --- /dev/null +++ b/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs @@ -0,0 +1,4 @@ +namespace Discord.Net.Udp +{ + public delegate IUdpSocket UdpSocketProvider(); +} diff --git a/src/Discord.Net.Core/Utils/DateTimeUtils.cs b/src/Discord.Net.Core/Utils/DateTimeUtils.cs index aa127fe29..b36d329d1 100644 --- a/src/Discord.Net.Core/Utils/DateTimeUtils.cs +++ b/src/Discord.Net.Core/Utils/DateTimeUtils.cs @@ -2,14 +2,58 @@ namespace Discord { -internal static class DateTimeUtils -{ - public static DateTimeOffset FromSnowflake(ulong value) - => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); + internal static class DateTimeUtils + { +#if !NETSTANDARD1_3 + //https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs + private const long UnixEpochTicks = 621355968000000000; + private const long UnixEpochSeconds = 62135596800; +#endif - public static DateTimeOffset FromTicks(long ticks) - => new DateTimeOffset(ticks, TimeSpan.Zero); - public static DateTimeOffset? FromTicks(long? ticks) - => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; -} + public static DateTimeOffset FromSnowflake(ulong value) + => FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); + + public static DateTimeOffset FromTicks(long ticks) + => new DateTimeOffset(ticks, TimeSpan.Zero); + public static DateTimeOffset? FromTicks(long? ticks) + => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; + + public static DateTimeOffset FromUnixSeconds(long seconds) + { +#if NETSTANDARD1_3 + return DateTimeOffset.FromUnixTimeSeconds(seconds); +#else + long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); +#endif + } + public static DateTimeOffset FromUnixMilliseconds(long seconds) + { +#if NETSTANDARD1_3 + return DateTimeOffset.FromUnixTimeMilliseconds(seconds); +#else + long ticks = seconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); +#endif + } + + public static long ToUnixSeconds(DateTimeOffset dto) + { +#if NETSTANDARD1_3 + return dto.ToUnixTimeSeconds(); +#else + long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond; + return seconds - UnixEpochSeconds; +#endif + } + public static long ToUnixMilliseconds(DateTimeOffset dto) + { +#if NETSTANDARD1_3 + return dto.ToUnixTimeMilliseconds(); +#else + long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; + return seconds - UnixEpochSeconds; +#endif + } + } } diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json index 6a62b9474..16a10f585 100644 --- a/src/Discord.Net.Core/project.json +++ b/src/Discord.Net.Core/project.json @@ -26,25 +26,16 @@ }, "dependencies": { - "Microsoft.Win32.Primitives": "4.3.0", "Newtonsoft.Json": "9.0.1", "System.Collections.Concurrent": "4.3.0", "System.Collections.Immutable": "1.3.0", "System.Interactive.Async": "3.1.0", "System.Net.Http": "4.3.0", - "System.Net.WebSockets.Client": { - "version": "4.3.0", - "type": "build" - } + "System.Runtime.Serialization.Primitives": "4.1.1" }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 4ed019dfb..1a0348d34 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -86,7 +86,7 @@ namespace Discord.Rest await _loggedInEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.CompletedTask; } + protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); } /// public async Task LogoutAsync() @@ -111,7 +111,7 @@ namespace Discord.Rest await _loggedOutEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLogoutAsync() { return Task.CompletedTask; } + protected virtual Task OnLogoutAsync() { return Task.Delay(0); } internal virtual void Dispose(bool disposing) { diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 6bc40571f..87a719538 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,44 +1,28 @@ - - + A core Discord.Net library containing the REST client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 8c1bd45da..bf45db07f 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -21,12 +21,12 @@ namespace Discord.Rest protected override Task OnLoginAsync(TokenType tokenType, string token) { base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); - return Task.CompletedTask; + return Task.Delay(0); } protected override Task OnLogoutAsync() { _applicationInfo = null; - return Task.CompletedTask; + return Task.Delay(0); } /// diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 99edc6f48..b64c288ee 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -139,6 +139,7 @@ namespace Discord.Rest return RestUserMessage.Create(client, channel, client.CurrentUser, model); } +#if NETSTANDARD1_3 public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, string filePath, string text, bool isTTS, RequestOptions options) { @@ -146,6 +147,7 @@ namespace Discord.Rest using (var file = File.OpenRead(filePath)) return await SendFileAsync(channel, client, file, filename, text, isTTS, options).ConfigureAwait(false); } +#endif public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, Stream stream, string filename, string text, bool isTTS, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index 554104d4d..7fbbd21c1 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -8,8 +8,10 @@ namespace Discord.Rest { /// Sends a message to this message channel. new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 573cfef72..5205d8221 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -65,8 +65,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -122,8 +124,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index e2a015c75..2892695c4 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -78,8 +78,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -132,8 +134,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 991b30283..c14899daa 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -57,8 +57,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -104,8 +106,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs index 6127eaf65..dc4178c3c 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs @@ -35,8 +35,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -82,8 +84,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index e7115babd..4f74129dd 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -69,7 +69,7 @@ namespace Discord.Rest public static ImmutableArray ParseTags(string text, IMessageChannel channel, IGuild guild, ImmutableArray userMentions) { - var tags = new SortedList(); + var tags = ImmutableArray.CreateBuilder(); int index = 0; while (true) @@ -94,27 +94,27 @@ namespace Discord.Rest break; } } - tags.Add(index, new Tag(TagType.UserMention, index, content.Length, id, mentionedUser)); + tags.Add(new Tag(TagType.UserMention, index, content.Length, id, mentionedUser)); } else if (MentionUtils.TryParseChannel(content, out id)) { IChannel mentionedChannel = null; if (guild != null) mentionedChannel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult(); - tags.Add(index, new Tag(TagType.ChannelMention, index, content.Length, id, mentionedChannel)); + tags.Add(new Tag(TagType.ChannelMention, index, content.Length, id, mentionedChannel)); } else if (MentionUtils.TryParseRole(content, out id)) { IRole mentionedRole = null; if (guild != null) mentionedRole = guild.GetRole(id); - tags.Add(index, new Tag(TagType.RoleMention, index, content.Length, id, mentionedRole)); + tags.Add(new Tag(TagType.RoleMention, index, content.Length, id, mentionedRole)); } else { Emoji emoji; if (Emoji.TryParse(content, out emoji)) - tags.Add(index, new Tag(TagType.Emoji, index, content.Length, id, emoji)); + tags.Add(new Tag(TagType.Emoji, index, content.Length, id, emoji)); } index = endIndex + 1; } @@ -125,7 +125,7 @@ namespace Discord.Rest index = text.IndexOf("@everyone", index); if (index == -1) break; - tags.Add(index, new Tag(TagType.EveryoneMention, index, "@everyone".Length, 0, null)); + tags.Add(new Tag(TagType.EveryoneMention, index, "@everyone".Length, 0, null)); index++; } @@ -135,11 +135,11 @@ namespace Discord.Rest index = text.IndexOf("@here", index); if (index == -1) break; - tags.Add(index, new Tag(TagType.HereMention, index, "@here".Length, 0, null)); + tags.Add(new Tag(TagType.HereMention, index, "@here".Length, 0, null)); index++; } - return tags.Values.ToImmutableArray(); + return tags.ToImmutable(); } public static ImmutableArray FilterTagsByKey(TagType type, ImmutableArray tags) { diff --git a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs similarity index 99% rename from src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs rename to src/Discord.Net.Rest/Net/DefaultRestClient.cs index 588785230..9915ffd53 100644 --- a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs +++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace Discord.Net.Rest { - public sealed class DefaultRestClient : IRestClient + internal sealed class DefaultRestClient : IRestClient { private const int HR_SECURECHANNELFAILED = -2146233079; diff --git a/src/Discord.Net.Rest/project.json b/src/Discord.Net.Rest/project.json index b1ad15cb8..7db631b18 100644 --- a/src/Discord.Net.Rest/project.json +++ b/src/Discord.Net.Rest/project.json @@ -29,16 +29,14 @@ "Discord.Net.Core": { "target": "project" }, - "System.IO.FileSystem": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.IO.FileSystem": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 050783f28..720c975c0 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -218,9 +218,6 @@ namespace Discord.API private async Task SendRpcAsyncInternal(string cmd, object payload, Optional evt, RequestOptions options) where TResponse : class { - if (!options.IgnoreState) - CheckState(); - byte[] bytes = null; var guid = Guid.NewGuid(); payload = new API.Rpc.RpcFrame { Cmd = cmd, Event = evt, Args = payload, Nonce = guid }; diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index efd2ea893..4b0ce4c5d 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,48 +1,40 @@ - - + A core Discord.Net library containing the RPC client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - + + + Net\DefaultWebSocketClient.cs + + - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 4.3.0 - + + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Rpc/DiscordRpcConfig.cs b/src/Discord.Net.Rpc/DiscordRpcConfig.cs index d1e69376c..1866e838b 100644 --- a/src/Discord.Net.Rpc/DiscordRpcConfig.cs +++ b/src/Discord.Net.Rpc/DiscordRpcConfig.cs @@ -1,5 +1,6 @@ using Discord.Net.WebSockets; using Discord.Rest; +using System; namespace Discord.Rpc { @@ -14,6 +15,19 @@ namespace Discord.Rpc public int ConnectionTimeout { get; set; } = 30000; /// Gets or sets the provider used to generate new websocket connections. - public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); + public WebSocketProvider WebSocketProvider { get; set; } + + public DiscordRpcConfig() + { +#if NETSTANDARD1_3 + WebSocketProvider = () => new DefaultWebSocketClient(); +#else + WebSocketProvider = () => + { + throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } } } diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs index af0102574..ba0a31910 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs @@ -46,8 +46,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -100,8 +102,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs index c88621d8f..599df7e50 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs @@ -48,8 +48,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -99,8 +101,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs index a5779bdbb..08780debb 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -51,8 +51,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -101,8 +103,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json index 8b2db7421..d258fbdcb 100644 --- a/src/Discord.Net.Rpc/project.json +++ b/src/Discord.Net.Rpc/project.json @@ -32,17 +32,15 @@ "Discord.Net.Rest": { "target": "project" }, - "System.IO.Compression": "4.3.0", - "System.Net.WebSockets.Client": "4.3.0" + "System.IO.Compression": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.Net.WebSockets.Client": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs index 378acd22e..adbffc780 100644 --- a/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs @@ -2,6 +2,7 @@ using Discord.API; using Discord.API.Voice; using Discord.Net.Converters; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Newtonsoft.Json; using System; @@ -9,8 +10,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; -using System.Net; -using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -42,19 +41,27 @@ namespace Discord.Audio private readonly IWebSocketClient _webSocketClient; private readonly SemaphoreSlim _connectionLock; private CancellationTokenSource _connectCancelToken; - private UdpClient _udp; - private IPEndPoint _udpEndpoint; - private Task _udpRecieveTask; + private IUdpSocket _udp; private bool _isDisposed; public ulong GuildId { get; } public ConnectionState ConnectionState { get; private set; } - internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, JsonSerializer serializer = null) + internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, UdpSocketProvider udpSocketProvider, JsonSerializer serializer = null) { GuildId = guildId; _connectionLock = new SemaphoreSlim(1, 1); - _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); + _udp = udpSocketProvider(); + _udp.ReceivedDatagram += async (data, index, count) => + { + if (index != 0) + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _receivedPacketEvent.InvokeAsync(data).ConfigureAwait(false); + }; _webSocketClient = webSocketProvider(); //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+) @@ -93,6 +100,7 @@ namespace Discord.Audio if (disposing) { _connectCancelToken?.Dispose(); + (_udp as IDisposable)?.Dispose(); (_webSocketClient as IDisposable)?.Dispose(); } _isDisposed = true; @@ -111,18 +119,14 @@ namespace Discord.Audio } public async Task SendAsync(byte[] data, int bytes) { - if (_udpEndpoint != null) - { - await _udp.SendAsync(data, bytes, _udpEndpoint).ConfigureAwait(false); - await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false); - } - + await _udp.SendAsync(data, 0, bytes).ConfigureAwait(false); + await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false); } //WebSocket public async Task SendHeartbeatAsync(RequestOptions options = null) { - await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false); + await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false); } public async Task SendIdentityAsync(ulong userId, string sessionId, string token) { @@ -171,9 +175,13 @@ namespace Discord.Audio try { _connectCancelToken = new CancellationTokenSource(); - _webSocketClient.SetCancelToken(_connectCancelToken.Token); + var cancelToken = _connectCancelToken.Token; + + _webSocketClient.SetCancelToken(cancelToken); await _webSocketClient.ConnectAsync(url).ConfigureAwait(false); - _udpRecieveTask = ReceiveAsync(_connectCancelToken.Token); + + _udp.SetCancelToken(cancelToken); + await _udp.StartAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Connected; } @@ -202,8 +210,7 @@ namespace Discord.Audio catch { } //Wait for tasks to complete - await _udpRecieveTask.ConfigureAwait(false); - + await _udp.StopAsync().ConfigureAwait(false); await _webSocketClient.DisconnectAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Disconnected; @@ -221,22 +228,9 @@ namespace Discord.Audio await _sentDiscoveryEvent.InvokeAsync().ConfigureAwait(false); } - public void SetUdpEndpoint(IPEndPoint endpoint) + public void SetUdpEndpoint(string host, int port) { - _udpEndpoint = endpoint; - } - private async Task ReceiveAsync(CancellationToken cancelToken) - { - var closeTask = Task.Delay(-1, cancelToken); - while (!cancelToken.IsCancellationRequested) - { - var receiveTask = _udp.ReceiveAsync(); - var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); - if (task == closeTask) - break; - - await _receivedPacketEvent.InvokeAsync(receiveTask.Result.Buffer).ConfigureAwait(false); - } + _udp.SetDestination(host, port); } //Helpers diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 04eec4541..a3774a76c 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -71,7 +71,7 @@ namespace Discord.Audio e.ErrorContext.Handled = true; }; - ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider); + ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); @@ -204,10 +204,8 @@ namespace Discord.Audio _heartbeatTime = 0; _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token); - - var entry = await Dns.GetHostEntryAsync(_url).ConfigureAwait(false); - - ApiClient.SetUdpEndpoint(new IPEndPoint(entry.AddressList[0], data.Port)); + + ApiClient.SetUdpEndpoint(_url, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); } break; diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index dd6541412..c7ca40c13 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,9 +1,8 @@ - - + A core Discord.Net library containing the WebSocket client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 true Discord.Net.WebSocket discord;discordapp @@ -11,48 +10,27 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - + + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index c28e30b51..a61d21214 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -4,6 +4,7 @@ using Discord.Audio; using Discord.Logging; using Discord.Net.Converters; using Discord.Net.Queue; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; using Newtonsoft.Json; @@ -56,6 +57,7 @@ namespace Discord.WebSocket internal AudioMode AudioMode { get; private set; } internal ClientState State { get; private set; } internal int ConnectionTimeout { get; private set; } + internal UdpSocketProvider UdpSocketProvider { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; } internal bool DownloadUsersOnGuildAvailable { get; private set; } @@ -76,6 +78,7 @@ namespace Discord.WebSocket MessageCacheSize = config.MessageCacheSize; LargeThreshold = config.LargeThreshold; AudioMode = config.AudioMode; + UdpSocketProvider = config.UdpSocketProvider; WebSocketProvider = config.WebSocketProvider; DownloadUsersOnGuildAvailable = config.DownloadUsersOnGuildAvailable; ConnectionTimeout = config.ConnectionTimeout; @@ -115,7 +118,7 @@ namespace Discord.WebSocket GuildAvailable += g => { var _ = g.DownloadUsersAsync(); - return Task.CompletedTask; + return Task.Delay(0); }; } @@ -512,7 +515,7 @@ namespace Discord.WebSocket await ApiClient.SendStatusUpdateAsync( status, status == UserStatus.AFK, - statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, + statusSince != null ? DateTimeUtils.ToUnixMilliseconds(_statusSince.Value) : (long?)null, gameModel).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 9bf337f8e..04001a5aa 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -1,6 +1,8 @@ using Discord.Audio; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; +using System; namespace Discord.WebSocket { @@ -27,9 +29,30 @@ namespace Discord.WebSocket public AudioMode AudioMode { get; set; } = AudioMode.Disabled; /// Gets or sets the provider used to generate new websocket connections. - public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); + public WebSocketProvider WebSocketProvider { get; set; } + /// Gets or sets the provider used to generate new udp sockets. + public UdpSocketProvider UdpSocketProvider { get; set; } /// Gets or sets whether or not all users should be downloaded as guilds come available. public bool DownloadUsersOnGuildAvailable { get; set; } = false; + + public DiscordSocketConfig() + { +#if NETSTANDARD1_3 + WebSocketProvider = () => new DefaultWebSocketClient(); + UdpSocketProvider = () => new DefaultUdpSocket(); +#else + WebSocketProvider = () => + { + throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; + UdpSocketProvider = () => + { + throw new InvalidOperationException("The default UDP provider is not supported on this platform.\n" + + "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index f1d4221a2..0f8e1e156 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -12,8 +12,10 @@ namespace Discord.WebSocket /// Sends a message to this message channel. new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 48f87764b..34f67c1f0 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -68,8 +68,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -130,8 +132,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 93407e22e..980b6dc2a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -91,8 +91,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -193,8 +195,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 9b687c9fb..903263e89 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -74,8 +74,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -131,8 +133,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs new file mode 100644 index 000000000..050833fb8 --- /dev/null +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -0,0 +1,129 @@ +#if NETSTANDARD1_3 +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Udp +{ + internal class DefaultUdpSocket : IUdpSocket + { + public event Func ReceivedDatagram; + + private readonly SemaphoreSlim _lock; + private UdpClient _udp; + private IPEndPoint _destination; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private Task _task; + private bool _isDisposed; + + public DefaultUdpSocket() + { + _lock = new SemaphoreSlim(1, 1); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + StopInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + + public async Task StartAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StartInternalAsync(_cancelToken).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StartInternalAsync(CancellationToken cancelToken) + { + await StopInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _udp = new UdpClient(); + + _task = RunAsync(_cancelToken); + } + public async Task StopAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StopInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StopInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_udp != null) + { + try { _udp.Dispose(); } + catch { } + _udp = null; + } + } + + public void SetDestination(string host, int port) + { + var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult(); + _destination = new IPEndPoint(entry.AddressList[0], port); + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count) + { + if (index != 0) //Should never happen? + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _udp.SendAsync(data, count, _destination).ConfigureAwait(false); + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var closeTask = Task.Delay(-1, cancelToken); + while (!cancelToken.IsCancellationRequested) + { + var receiveTask = _udp.ReceiveAsync(); + var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); + if (task == closeTask) + break; + + var result = receiveTask.Result; + await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs similarity index 87% rename from src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs rename to src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index ffa96dba7..acfff94f2 100644 --- a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -1,4 +1,5 @@ -using System; +#if NETSTANDARD1_3 +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -9,7 +10,7 @@ using System.Threading.Tasks; namespace Discord.Net.WebSockets { - public class DefaultWebSocketClient : IWebSocketClient + internal class DefaultWebSocketClient : IWebSocketClient { public const int ReceiveChunkSize = 16 * 1024; //16KB public const int SendChunkSize = 4 * 1024; //4KB @@ -19,7 +20,7 @@ namespace Discord.Net.WebSockets public event Func TextMessage; public event Func Closed; - private readonly SemaphoreSlim _sendLock; + private readonly SemaphoreSlim _lock; private readonly Dictionary _headers; private ClientWebSocket _client; private Task _task; @@ -29,7 +30,7 @@ namespace Discord.Net.WebSockets public DefaultWebSocketClient() { - _sendLock = new SemaphoreSlim(1, 1); + _lock = new SemaphoreSlim(1, 1); _cancelTokenSource = new CancellationTokenSource(); _cancelToken = CancellationToken.None; _parentToken = CancellationToken.None; @@ -40,7 +41,7 @@ namespace Discord.Net.WebSockets if (!_isDisposed) { if (disposing) - _client.Dispose(); + DisconnectInternalAsync(true).GetAwaiter().GetResult(); _isDisposed = true; } } @@ -51,14 +52,14 @@ namespace Discord.Net.WebSockets public async Task ConnectAsync(string host) { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { await ConnectInternalAsync(host).ConfigureAwait(false); } finally { - _sendLock.Release(); + _lock.Release(); } } private async Task ConnectInternalAsync(string host) @@ -83,27 +84,33 @@ namespace Discord.Net.WebSockets public async Task DisconnectAsync() { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { await DisconnectInternalAsync().ConfigureAwait(false); } finally { - _sendLock.Release(); + _lock.Release(); } } - private async Task DisconnectInternalAsync() + private async Task DisconnectInternalAsync(bool isDisposing = false) { try { _cancelTokenSource.Cancel(false); } catch { } - await (_task ?? Task.CompletedTask).ConfigureAwait(false); + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); if (_client != null && _client.State == WebSocketState.Open) { var token = new CancellationToken(); - await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", token); - _client.Dispose(); + if (!isDisposing) + { + try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", token); } + catch { } + } + try { _client.Dispose(); } + catch { } _client = null; } } @@ -120,7 +127,7 @@ namespace Discord.Net.WebSockets public async Task SendAsync(byte[] data, int index, int count, bool isText) { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { if (_client == null) return; @@ -143,7 +150,7 @@ namespace Discord.Net.WebSockets } finally { - _sendLock.Release(); + _lock.Release(); } } @@ -181,11 +188,15 @@ namespace Discord.Net.WebSockets //Use the internal buffer if we can get it resultCount = (int)stream.Length; +#if NETSTANDARD1_3 ArraySegment streamBuffer; if (stream.TryGetBuffer(out streamBuffer)) result = streamBuffer.Array; else result = stream.ToArray(); +#else + result = stream.ToArray(); +#endif } } else @@ -217,3 +228,4 @@ namespace Discord.Net.WebSockets } } } +#endif \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/project.json b/src/Discord.Net.WebSocket/project.json index 46eb0eccd..4ee07e2ac 100644 --- a/src/Discord.Net.WebSocket/project.json +++ b/src/Discord.Net.WebSocket/project.json @@ -37,19 +37,17 @@ "target": "project" }, "System.IO.Compression": "4.3.0", - "System.Net.NameResolution": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Net.WebSockets.Client": "4.3.0", "System.Runtime.InteropServices": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.Net.NameResolution": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.Net.WebSockets.Client": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index ae9eb3e2a..be5f09125 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,23 +1,19 @@ - - + An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - @@ -25,18 +21,4 @@ - - - 1.0.0-alpha-20161104-2 - All - - - - - False - - - $(DefineConstants);RELEASE - - \ No newline at end of file diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 98b3b6211..c02f510f9 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -32,12 +32,7 @@ }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } \ No newline at end of file