Browse Source

Merged Discord.Net.Audio into the main project, starting adding IVoiceChannel.ConnectAsync()

tags/1.0-rc
RogueException 9 years ago
parent
commit
58d54a76b5
31 changed files with 191 additions and 222 deletions
  1. +0
    -17
      src/Discord.Net.Audio/AudioConfig.cs
  2. +0
    -18
      src/Discord.Net.Audio/Discord.Net.Audio.xproj
  3. +0
    -6
      src/Discord.Net.Audio/Logger.cs
  4. +0
    -74
      src/Discord.Net.Audio/Utilities/AsyncEvent.cs
  5. +0
    -35
      src/Discord.Net.Audio/project.json
  6. +11
    -0
      src/Discord.Net/API/DiscordAPIClient.cs
  7. +2
    -2
      src/Discord.Net/API/DiscordVoiceAPIClient.cs
  8. +12
    -0
      src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs
  9. +6
    -2
      src/Discord.Net/API/Gateway/RequestMembersParams.cs
  10. +0
    -16
      src/Discord.Net/API/Gateway/UpdateVoiceParams.cs
  11. +21
    -0
      src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs
  12. +10
    -14
      src/Discord.Net/Audio/AudioClient.cs
  13. +5
    -1
      src/Discord.Net/Audio/AudioMode.cs
  14. +20
    -0
      src/Discord.Net/Audio/IAudioClient.cs
  15. +0
    -0
      src/Discord.Net/Audio/Opus/Ctl.cs
  16. +0
    -0
      src/Discord.Net/Audio/Opus/OpusApplication.cs
  17. +0
    -0
      src/Discord.Net/Audio/Opus/OpusConverter.cs
  18. +0
    -0
      src/Discord.Net/Audio/Opus/OpusDecoder.cs
  19. +1
    -1
      src/Discord.Net/Audio/Opus/OpusEncoder.cs
  20. +0
    -0
      src/Discord.Net/Audio/Opus/OpusError.cs
  21. +2
    -2
      src/Discord.Net/Audio/Sodium/SecretBox.cs
  22. +1
    -0
      src/Discord.Net/DiscordSocketClient.Events.cs
  23. +52
    -30
      src/Discord.Net/DiscordSocketClient.cs
  24. +6
    -2
      src/Discord.Net/DiscordSocketConfig.cs
  25. +3
    -0
      src/Discord.Net/Entities/Channels/IVoiceChannel.cs
  26. +3
    -0
      src/Discord.Net/Entities/Channels/VoiceChannel.cs
  27. +2
    -0
      src/Discord.Net/Entities/Guilds/Guild.cs
  28. +3
    -0
      src/Discord.Net/Entities/Guilds/IGuild.cs
  29. +14
    -1
      src/Discord.Net/Entities/WebSocket/CachedGuild.cs
  30. +16
    -1
      src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs
  31. +1
    -0
      src/Discord.Net/project.json

+ 0
- 17
src/Discord.Net.Audio/AudioConfig.cs View File

@@ -1,17 +0,0 @@
using Discord.Net.WebSockets;

namespace Discord.Audio
{
public class AudioConfig
{
/// <summary> Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize. </summary>
public int ConnectionTimeout { get; set; } = 30000;
/// <summary> Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. </summary>
public int ReconnectDelay { get; set; } = 1000;
/// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary>
public int FailedReconnectDelay { get; set; } = 15000;

/// <summary> Gets or sets the provider used to generate new websocket connections. </summary>
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient();
}
}

+ 0
- 18
src/Discord.Net.Audio/Discord.Net.Audio.xproj View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>ddfcc44f-934e-478a-978c-69cdda2a1c5b</ProjectGuid>
<RootNamespace>Discord.Audio</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

+ 0
- 6
src/Discord.Net.Audio/Logger.cs View File

@@ -1,6 +0,0 @@
namespace Discord.Audio
{
internal class Logger
{
}
}

+ 0
- 74
src/Discord.Net.Audio/Utilities/AsyncEvent.cs View File

@@ -1,74 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;

namespace Discord
{
public class AsyncEvent<T>
{
private readonly object _subLock = new object();
internal ImmutableArray<T> _subscriptions;

public IReadOnlyList<T> Subscriptions => _subscriptions;

public AsyncEvent()
{
_subscriptions = ImmutableArray.Create<T>();
}

public void Add(T subscriber)
{
lock (_subLock)
_subscriptions = _subscriptions.Add(subscriber);
}
public void Remove(T subscriber)
{
lock (_subLock)
_subscriptions = _subscriptions.Remove(subscriber);
}
}

public static class EventExtensions
{
public static async Task InvokeAsync(this AsyncEvent<Func<Task>> eventHandler)
{
var subscribers = eventHandler.Subscriptions;
if (subscribers.Count > 0)
{
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke().ConfigureAwait(false);
}
}
public static async Task InvokeAsync<T>(this AsyncEvent<Func<T, Task>> eventHandler, T arg)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2>(this AsyncEvent<Func<T1, T2, Task>> eventHandler, T1 arg1, T2 arg2)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3>(this AsyncEvent<Func<T1, T2, T3, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3, T4>(this AsyncEvent<Func<T1, T2, T3, T4, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3, arg4).ConfigureAwait(false);
}
public static async Task InvokeAsync<T1, T2, T3, T4, T5>(this AsyncEvent<Func<T1, T2, T3, T4, T5, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
{
var subscribers = eventHandler.Subscriptions;
for (int i = 0; i < subscribers.Count; i++)
await subscribers[i].Invoke(arg1, arg2, arg3, arg4, arg5).ConfigureAwait(false);
}
}
}

+ 0
- 35
src/Discord.Net.Audio/project.json View File

@@ -1,35 +0,0 @@
{
"version": "1.0.0-dev",
"description": "A Discord.Net extension adding audio support.",
"authors": [ "RogueException" ],

"packOptions": {
"tags": [ "discord", "discordapp" ],
"licenseUrl": "http://opensource.org/licenses/MIT",
"projectUrl": "https://github.com/RogueException/Discord.Net",
"repository": {
"type": "git",
"url": "git://github.com/RogueException/Discord.Net"
}
},

"buildOptions": {
"allowUnsafe": true,
"warningsAsErrors": false
},

"dependencies": {
"Discord.Net": "1.0.0-dev",
"System.Runtime.InteropServices": "4.1.0"
},

"frameworks": {
"netstandard1.3": {
"imports": [
"dotnet5.4",
"dnxcore50",
"portable-net45+win8"
]
}
}
}

+ 11
- 0
src/Discord.Net/API/DiscordAPIClient.cs View File

@@ -398,6 +398,17 @@ namespace Discord.API
{ {
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false); await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
} }
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
{
var payload = new VoiceStateUpdateParams
{
GuildId = guildId,
ChannelId = channelId,
SelfDeaf = selfDeaf,
SelfMute = selfMute
};
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
}


//Channels //Channels
public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null) public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)


src/Discord.Net.Audio/AudioAPIClient.cs → src/Discord.Net/API/DiscordVoiceAPIClient.cs View File

@@ -14,7 +14,7 @@ using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
public class AudioAPIClient
public class DiscordVoiceAPIClient
{ {
public const int MaxBitrate = 128; public const int MaxBitrate = 128;
private const string Mode = "xsalsa20_poly1305"; private const string Mode = "xsalsa20_poly1305";
@@ -40,7 +40,7 @@ namespace Discord.Audio
public string SessionId { get; } public string SessionId { get; }
public ConnectionState ConnectionState { get; private set; } public ConnectionState ConnectionState { get; private set; }


internal AudioAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
internal DiscordVoiceAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
{ {
GuildId = guildId; GuildId = guildId;
_userId = userId; _userId = userId;

+ 12
- 0
src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
public class GuildEmojiUpdateEvent
{
[JsonProperty("guild_id")]
public ulong GuildId;
[JsonProperty("emojis")]
public Emoji[] Emojis;
}
}

+ 6
- 2
src/Discord.Net/API/Gateway/RequestMembersParams.cs View File

@@ -1,15 +1,19 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;


namespace Discord.API.Gateway namespace Discord.API.Gateway
{ {
public class RequestMembersParams public class RequestMembersParams
{ {
[JsonProperty("guild_id")]
public IEnumerable<ulong> GuildIds { get; set; }
[JsonProperty("query")] [JsonProperty("query")]
public string Query { get; set; } public string Query { get; set; }
[JsonProperty("limit")] [JsonProperty("limit")]
public int Limit { get; set; } public int Limit { get; set; }

[JsonProperty("guild_id")]
public IEnumerable<ulong> GuildIds { get; set; }
[JsonIgnore]
public IEnumerable<IGuild> Guilds { set { GuildIds = value.Select(x => x.Id); } }
} }
} }

+ 0
- 16
src/Discord.Net/API/Gateway/UpdateVoiceParams.cs View File

@@ -1,16 +0,0 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
public class UpdateVoiceParams
{
[JsonProperty("guild_id")]
public ulong? GuildId { get; set; }
[JsonProperty("channel_id")]
public ulong? ChannelId { get; set; }
[JsonProperty("self_mute")]
public bool IsSelfMuted { get; set; }
[JsonProperty("self_deaf")]
public bool IsSelfDeafened { get; set; }
}
}

+ 21
- 0
src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs View File

@@ -0,0 +1,21 @@
using Newtonsoft.Json;

namespace Discord.API.Gateway
{
public class VoiceStateUpdateParams
{
[JsonProperty("self_mute")]
public bool SelfMute { get; set; }
[JsonProperty("self_deaf")]
public bool SelfDeaf { get; set; }

[JsonProperty("guild_id")]
public ulong GuildId { get; set; }
[JsonIgnore]
public IGuild Guild { set { GuildId = value.Id; } }
[JsonProperty("channel_id")]
public ulong? ChannelId { get; set; }
[JsonIgnore]
public IChannel Channel { set { ChannelId = value?.Id; } }
}
}

src/Discord.Net.Audio/AudioClient.cs → src/Discord.Net/Audio/AudioClient.cs View File

@@ -1,15 +1,15 @@
using Discord.API.Voice; using Discord.API.Voice;
using Discord.Logging; using Discord.Logging;
using Discord.Net.Converters; using Discord.Net.Converters;
using Discord.Net.WebSockets;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord.Audio namespace Discord.Audio
{ {
public class AudioClient
internal class AudioClient : IAudioClient
{ {
public event Func<Task> Connected public event Func<Task> Connected
{ {
@@ -30,12 +30,11 @@ namespace Discord.Audio
} }
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>(); private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();


private readonly ILogger _webSocketLogger;
private readonly ILogger _webSocketLogger, _udpLogger;
#if BENCHMARK #if BENCHMARK
private readonly ILogger _benchmarkLogger; private readonly ILogger _benchmarkLogger;
#endif #endif
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
internal readonly SemaphoreSlim _connectionLock; internal readonly SemaphoreSlim _connectionLock;


private TaskCompletionSource<bool> _connectTask; private TaskCompletionSource<bool> _connectTask;
@@ -45,20 +44,18 @@ namespace Discord.Audio
private bool _isReconnecting; private bool _isReconnecting;
private string _url; private string _url;


public AudioAPIClient ApiClient { get; private set; }
/// <summary> Gets the current connection state of this client. </summary>
private DiscordSocketClient Discord { get; }
public DiscordVoiceAPIClient ApiClient { get; private set; }
public ConnectionState ConnectionState { get; private set; } public ConnectionState ConnectionState { get; private set; }
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
public int Latency { get; private set; } public int Latency { get; private set; }


/// <summary> Creates a new REST/WebSocket discord client. </summary> /// <summary> Creates a new REST/WebSocket discord client. </summary>
internal AudioClient(ulong guildId, ulong userId, string sessionId, string token, AudioConfig config, ILogManager logManager)
internal AudioClient(DiscordSocketClient discord, ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, ILogManager logManager)
{ {
_connectionTimeout = config.ConnectionTimeout;
_reconnectDelay = config.ReconnectDelay;
_failedReconnectDelay = config.FailedReconnectDelay;
Discord = discord;


_webSocketLogger = logManager.CreateLogger("AudioWS");
_webSocketLogger = logManager.CreateLogger("Audio");
_udpLogger = logManager.CreateLogger("AudioUDP");
#if BENCHMARK #if BENCHMARK
_benchmarkLogger = logManager.CreateLogger("Benchmark"); _benchmarkLogger = logManager.CreateLogger("Benchmark");
#endif #endif
@@ -72,8 +69,7 @@ namespace Discord.Audio
e.ErrorContext.Handled = true; e.ErrorContext.Handled = true;
}; };
var webSocketProvider = config.WebSocketProvider; //TODO: Clean this check
ApiClient = new AudioAPIClient(guildId, userId, sessionId, token, config.WebSocketProvider);
ApiClient = new DiscordVoiceAPIClient(guildId, userId, sessionId, token, webSocketProvider);


ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false); ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false);
ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedEvent += ProcessMessageAsync;

src/Discord.Net.Audio/AudioMode.cs → src/Discord.Net/Audio/AudioMode.cs View File

@@ -1,7 +1,11 @@
namespace Discord.Audio
using System;

namespace Discord.Audio
{ {
[Flags]
public enum AudioMode : byte public enum AudioMode : byte
{ {
Disabled = 0,
Outgoing = 1, Outgoing = 1,
Incoming = 2, Incoming = 2,
Both = Outgoing | Incoming Both = Outgoing | Incoming

+ 20
- 0
src/Discord.Net/Audio/IAudioClient.cs View File

@@ -0,0 +1,20 @@
using System;
using System.Threading.Tasks;

namespace Discord.Audio
{
public interface IAudioClient
{
event Func<Task> Connected;
event Func<Task> Disconnected;
event Func<int, int, Task> LatencyUpdated;

DiscordVoiceAPIClient ApiClient { get; }
/// <summary> Gets the current connection state of this client. </summary>
ConnectionState ConnectionState { get; }
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
int Latency { get; }

Task DisconnectAsync();
}
}

src/Discord.Net.Audio/Opus/Ctl.cs → src/Discord.Net/Audio/Opus/Ctl.cs View File


src/Discord.Net.Audio/Opus/OpusApplication.cs → src/Discord.Net/Audio/Opus/OpusApplication.cs View File


src/Discord.Net.Audio/Opus/OpusConverter.cs → src/Discord.Net/Audio/Opus/OpusConverter.cs View File


src/Discord.Net.Audio/Opus/OpusDecoder.cs → src/Discord.Net/Audio/Opus/OpusDecoder.cs View File


src/Discord.Net.Audio/Opus/OpusEncoder.cs → src/Discord.Net/Audio/Opus/OpusEncoder.cs View File

@@ -36,7 +36,7 @@ namespace Discord.Audio.Opus
{ {
if (channels != 1 && channels != 2) if (channels != 1 && channels != 2)
throw new ArgumentOutOfRangeException(nameof(channels)); throw new ArgumentOutOfRangeException(nameof(channels));
if (bitrate != null && (bitrate < 1 || bitrate > AudioAPIClient.MaxBitrate))
if (bitrate != null && (bitrate < 1 || bitrate > DiscordVoiceAPIClient.MaxBitrate))
throw new ArgumentOutOfRangeException(nameof(bitrate)); throw new ArgumentOutOfRangeException(nameof(bitrate));


OpusError error; OpusError error;

src/Discord.Net.Audio/Opus/OpusError.cs → src/Discord.Net/Audio/Opus/OpusError.cs View File


src/Discord.Net.Audio/LibSodium.cs → src/Discord.Net/Audio/Sodium/SecretBox.cs View File

@@ -1,8 +1,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;


namespace Discord.Net.Audio
namespace Discord.Net.Audio.Sodium
{ {
public unsafe static class LibSodium
public unsafe static class SecretBox
{ {
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)] [DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret); private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret);

+ 1
- 0
src/Discord.Net/DiscordSocketClient.Events.cs View File

@@ -3,6 +3,7 @@ using System.Threading.Tasks;


namespace Discord namespace Discord
{ {
//TODO: Add event docstrings
public partial class DiscordSocketClient public partial class DiscordSocketClient
{ {
//General //General


+ 52
- 30
src/Discord.Net/DiscordSocketClient.cs View File

@@ -1,7 +1,9 @@
using Discord.API.Gateway; using Discord.API.Gateway;
using Discord.Audio;
using Discord.Extensions; using Discord.Extensions;
using Discord.Logging; using Discord.Logging;
using Discord.Net.Converters; using Discord.Net.Converters;
using Discord.Net.WebSockets;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@@ -14,9 +16,6 @@ using System.Threading.Tasks;


namespace Discord namespace Discord
{ {
//TODO: Add event docstrings
//TODO: Add reconnect logic (+ensure the heartbeat task to shut down)
//TODO: Add resume logic
public partial class DiscordSocketClient : DiscordClient, IDiscordClient public partial class DiscordSocketClient : DiscordClient, IDiscordClient
{ {
private readonly ConcurrentQueue<ulong> _largeGuilds; private readonly ConcurrentQueue<ulong> _largeGuilds;
@@ -25,9 +24,6 @@ namespace Discord
private readonly ILogger _benchmarkLogger; private readonly ILogger _benchmarkLogger;
#endif #endif
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
private readonly int _largeThreshold;
private readonly int _totalShards;


private string _sessionId; private string _sessionId;
private int _lastSeq; private int _lastSeq;
@@ -46,9 +42,17 @@ namespace Discord
public ConnectionState ConnectionState { get; private set; } public ConnectionState ConnectionState { get; private set; }
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
public int Latency { get; private set; } public int Latency { get; private set; }

//From DiscordConfig
internal int TotalShards { get; private set; }
internal int ConnectionTimeout { get; private set; }
internal int ReconnectDelay { get; private set; }
internal int FailedReconnectDelay { get; private set; }
internal int MessageCacheSize { get; private set; } internal int MessageCacheSize { get; private set; }
internal int LargeThreshold { get; private set; }
internal AudioMode AudioMode { get; private set; }
internal DataStore DataStore { get; private set; } internal DataStore DataStore { get; private set; }
internal WebSocketProvider WebSocketProvider { get; private set; }


internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser; internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds; internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
@@ -62,15 +66,15 @@ namespace Discord
: base(config) : base(config)
{ {
ShardId = config.ShardId; ShardId = config.ShardId;
_totalShards = config.TotalShards;

_connectionTimeout = config.ConnectionTimeout;
_reconnectDelay = config.ReconnectDelay;
_failedReconnectDelay = config.FailedReconnectDelay;

TotalShards = config.TotalShards;
ConnectionTimeout = config.ConnectionTimeout;
ReconnectDelay = config.ReconnectDelay;
FailedReconnectDelay = config.FailedReconnectDelay;
MessageCacheSize = config.MessageCacheSize; MessageCacheSize = config.MessageCacheSize;
_largeThreshold = config.LargeThreshold;
LargeThreshold = config.LargeThreshold;
AudioMode = config.AudioMode;
WebSocketProvider = config.WebSocketProvider;

_gatewayLogger = _log.CreateLogger("Gateway"); _gatewayLogger = _log.CreateLogger("Gateway");
#if BENCHMARK #if BENCHMARK
_benchmarkLogger = _log.CreateLogger("Benchmark"); _benchmarkLogger = _log.CreateLogger("Benchmark");
@@ -471,7 +475,7 @@ namespace Discord
case GatewayOpCode.Dispatch: case GatewayOpCode.Dispatch:
switch (type) switch (type)
{ {
//Global
//Connection
case "READY": case "READY":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
@@ -507,6 +511,11 @@ namespace Discord
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
} }
break; break;
case "RESUMED":
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);

await _gatewayLogger.InfoAsync("Resume").ConfigureAwait(false);
return;


//Guilds //Guilds
case "GUILD_CREATE": case "GUILD_CREATE":
@@ -569,6 +578,28 @@ namespace Discord
} }
} }
break; break;
case "GUILD_EMOJI_UPDATE": //TODO: Add
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJI_UPDATE)").ConfigureAwait(false);

var data = (payload as JToken).ToObject<API.Gateway.GuildEmojiUpdateEvent>(_serializer);
var guild = DataStore.GetGuild(data.GuildId);
if (guild != null)
{
var before = guild.Clone();
guild.Update(data, UpdateSource.WebSocket);
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
}
else
{
await _gatewayLogger.WarningAsync("GUILD_EMOJI_UPDATE referenced an unknown guild.").ConfigureAwait(false);
return;
}
}
return;
case "GUILD_INTEGRATIONS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
return;
case "GUILD_DELETE": case "GUILD_DELETE":
{ {
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer); var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
@@ -1099,26 +1130,17 @@ namespace Discord
} }
} }
break; break;
case "VOICE_SERVER_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
return;


//Ignored
//Ignored (User only)
case "USER_SETTINGS_UPDATE": case "USER_SETTINGS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
return; return;
case "MESSAGE_ACK": //TODO: Add (User only)
case "MESSAGE_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
return; return;
case "GUILD_EMOJIS_UPDATE": //TODO: Add
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);
return;
case "GUILD_INTEGRATIONS_UPDATE": //TODO: Add
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
return;
case "VOICE_SERVER_UPDATE": //TODO: Add
await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
return;
case "RESUMED": //TODO: Add
await _gatewayLogger.DebugAsync("Ignored Dispatch (RESUMED)").ConfigureAwait(false);
return;


//Others //Others
default: default:


+ 6
- 2
src/Discord.Net/DiscordSocketConfig.cs View File

@@ -1,4 +1,5 @@
using Discord.Net.WebSockets;
using Discord.Audio;
using Discord.Net.WebSockets;


namespace Discord namespace Discord
{ {
@@ -28,7 +29,10 @@ namespace Discord
/// Decreasing this may reduce CPU usage while increasing login time and network usage. /// Decreasing this may reduce CPU usage while increasing login time and network usage.
/// </summary> /// </summary>
public int LargeThreshold { get; set; } = 250; public int LargeThreshold { get; set; } = 250;

/// <summary> Gets or sets the type of audio this DiscordClient supports. </summary>
public AudioMode AudioMode { get; set; } = AudioMode.Disabled;

/// <summary> Gets or sets the provider used to generate new websocket connections. </summary> /// <summary> Gets or sets the provider used to generate new websocket connections. </summary>
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient();
} }


+ 3
- 0
src/Discord.Net/Entities/Channels/IVoiceChannel.cs View File

@@ -1,4 +1,5 @@
using Discord.API.Rest; using Discord.API.Rest;
using Discord.Audio;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;


@@ -13,5 +14,7 @@ namespace Discord


/// <summary> Modifies this voice channel. </summary> /// <summary> Modifies this voice channel. </summary>
Task ModifyAsync(Action<ModifyVoiceChannelParams> func); Task ModifyAsync(Action<ModifyVoiceChannelParams> func);
/// <summary> Connects to this voice channel. </summary>
Task<IAudioClient> ConnectAsync();
} }
} }

+ 3
- 0
src/Discord.Net/Entities/Channels/VoiceChannel.cs View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.Channel; using Model = Discord.API.Channel;
using Discord.Audio;


namespace Discord namespace Discord
{ {
@@ -49,6 +50,8 @@ namespace Discord
throw new NotSupportedException(); throw new NotSupportedException();
} }


public virtual Task<IAudioClient> ConnectAsync() { throw new NotSupportedException(); }

private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
} }
} }

+ 2
- 0
src/Discord.Net/Entities/Guilds/Guild.cs View File

@@ -1,4 +1,5 @@
using Discord.API.Rest; using Discord.API.Rest;
using Discord.Audio;
using Discord.Extensions; using Discord.Extensions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -311,6 +312,7 @@ namespace Discord
IReadOnlyCollection<Emoji> IGuild.Emojis => Emojis; IReadOnlyCollection<Emoji> IGuild.Emojis => Emojis;
IReadOnlyCollection<string> IGuild.Features => Features; IReadOnlyCollection<string> IGuild.Features => Features;
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
IAudioClient IGuild.AudioClient => null;


IRole IGuild.GetRole(ulong id) => GetRole(id); IRole IGuild.GetRole(ulong id) => GetRole(id);
} }


+ 3
- 0
src/Discord.Net/Entities/Guilds/IGuild.cs View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.API.Rest; using Discord.API.Rest;
using Discord.Audio;


namespace Discord namespace Discord
{ {
@@ -37,6 +38,8 @@ namespace Discord
/// <summary> Gets the id of the region hosting this guild's voice channels. </summary> /// <summary> Gets the id of the region hosting this guild's voice channels. </summary>
string VoiceRegionId { get; } string VoiceRegionId { get; }


/// <summary> Gets the IAudioClient currently associated with this guild. </summary>
IAudioClient AudioClient { get; }
/// <summary> Gets the built-in role containing all users in this guild. </summary> /// <summary> Gets the built-in role containing all users in this guild. </summary>
IRole EveryoneRole { get; } IRole EveryoneRole { get; }
/// <summary> Gets a collection of all custom emojis for this guild. </summary> /// <summary> Gets a collection of all custom emojis for this guild. </summary>


+ 14
- 1
src/Discord.Net/Entities/WebSocket/CachedGuild.cs View File

@@ -1,4 +1,5 @@
using Discord.Extensions;
using Discord.Audio;
using Discord.Extensions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -6,6 +7,7 @@ using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using ChannelModel = Discord.API.Channel; using ChannelModel = Discord.API.Channel;
using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent;
using ExtendedModel = Discord.API.Gateway.ExtendedGuild; using ExtendedModel = Discord.API.Gateway.ExtendedGuild;
using MemberModel = Discord.API.GuildMember; using MemberModel = Discord.API.GuildMember;
using Model = Discord.API.Guild; using Model = Discord.API.Guild;
@@ -25,6 +27,7 @@ namespace Discord
public bool Available { get; private set; } public bool Available { get; private set; }
public int MemberCount { get; private set; } public int MemberCount { get; private set; }
public int DownloadedMemberCount { get; private set; } public int DownloadedMemberCount { get; private set; }
public IAudioClient AudioClient { get; private set; }


public bool HasAllMembers => _downloaderPromise.Task.IsCompleted; public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
public Task DownloaderPromise => _downloaderPromise.Task; public Task DownloaderPromise => _downloaderPromise.Task;
@@ -102,6 +105,16 @@ namespace Discord
_voiceStates = voiceStates; _voiceStates = voiceStates;
} }


public void Update(EmojiUpdateModel model, UpdateSource source)
{
if (source == UpdateSource.Rest && IsAttached) return;
var emojis = ImmutableArray.CreateBuilder<Emoji>(model.Emojis.Length);
for (int i = 0; i < model.Emojis.Length; i++)
emojis.Add(new Emoji(model.Emojis[i]));
Emojis = emojis.ToImmutableArray();
}

public override Task<IGuildChannel> GetChannelAsync(ulong id) => Task.FromResult<IGuildChannel>(GetChannel(id)); public override Task<IGuildChannel> GetChannelAsync(ulong id) => Task.FromResult<IGuildChannel>(GetChannel(id));
public override Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync() => Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels); public override Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync() => Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels);
public void AddChannel(ChannelModel model, DataStore dataStore, ConcurrentHashSet<ulong> channels = null) public void AddChannel(ChannelModel model, DataStore dataStore, ConcurrentHashSet<ulong> channels = null)


+ 16
- 1
src/Discord.Net/Entities/WebSocket/CachedVoiceChannel.cs View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using Discord.Audio;
using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -33,6 +35,19 @@ namespace Discord
return null; return null;
} }


public override async Task<IAudioClient> ConnectAsync()
{
var audioMode = Discord.AudioMode;
if (audioMode == AudioMode.Disabled)
throw new InvalidOperationException($"Audio is not enabled on this client, {nameof(DiscordSocketConfig.AudioMode)} in {nameof(DiscordSocketConfig)} must be set.");

await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, Id,
(audioMode & AudioMode.Incoming) == 0,
(audioMode & AudioMode.Outgoing) == 0).ConfigureAwait(false);
return null;
//TODO: Block and return
}

public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel; public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel;


ICachedChannel ICachedChannel.Clone() => Clone(); ICachedChannel ICachedChannel.Clone() => Clone();


+ 1
- 0
src/Discord.Net/project.json View File

@@ -28,6 +28,7 @@
"System.Net.Http": "4.1.0", "System.Net.Http": "4.1.0",
"System.Net.WebSockets.Client": "4.0.0", "System.Net.WebSockets.Client": "4.0.0",
"System.Reflection.Extensions": "4.0.1", "System.Reflection.Extensions": "4.0.1",
"System.Runtime.InteropServices": "4.1.0",
"System.Runtime.Serialization.Primitives": "4.1.1", "System.Runtime.Serialization.Primitives": "4.1.1",
"System.Text.RegularExpressions": "4.1.0" "System.Text.RegularExpressions": "4.1.0"
}, },


Loading…
Cancel
Save