Browse Source

Added multi-server voice support

tags/docs-0.9
RogueException 9 years ago
parent
commit
d49542513c
9 changed files with 271 additions and 138 deletions
  1. +9
    -9
      src/Discord.Net.Net45/Discord.Net.csproj
  2. +113
    -42
      src/Discord.Net/DiscordClient.cs
  3. +9
    -2
      src/Discord.Net/DiscordClientConfig.cs
  4. +14
    -4
      src/Discord.Net/DiscordSimpleClient.Events.cs
  5. +26
    -20
      src/Discord.Net/DiscordSimpleClient.Voice.cs
  6. +74
    -43
      src/Discord.Net/DiscordSimpleClient.cs
  7. +1
    -1
      src/Discord.Net/WebSockets/Data/DataWebSocket.cs
  8. +23
    -15
      src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs
  9. +2
    -2
      src/Discord.Net/WebSockets/WebSocket.cs

+ 9
- 9
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -136,15 +136,6 @@
<Compile Include="..\Discord.Net\DiscordAPIClient.cs"> <Compile Include="..\Discord.Net\DiscordAPIClient.cs">
<Link>DiscordAPIClient.cs</Link> <Link>DiscordAPIClient.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\DiscordBaseClient.cs">
<Link>DiscordBaseClient.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordBaseClient.Events.cs">
<Link>DiscordBaseClient.Events.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordBaseClient.Voice.cs">
<Link>DiscordBaseClient.Voice.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordClient.API.cs"> <Compile Include="..\Discord.Net\DiscordClient.API.cs">
<Link>DiscordClient.API.cs</Link> <Link>DiscordClient.API.cs</Link>
</Compile> </Compile>
@@ -160,6 +151,15 @@
<Compile Include="..\Discord.Net\DiscordClientConfig.cs"> <Compile Include="..\Discord.Net\DiscordClientConfig.cs">
<Link>DiscordClientConfig.cs</Link> <Link>DiscordClientConfig.cs</Link>
</Compile> </Compile>
<Compile Include="..\Discord.Net\DiscordSimpleClient.cs">
<Link>DiscordSimpleClient.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordSimpleClient.Events.cs">
<Link>DiscordSimpleClient.Events.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\DiscordSimpleClient.Voice.cs">
<Link>DiscordSimpleClient.Voice.cs</Link>
</Compile>
<Compile Include="..\Discord.Net\Enums\ChannelTypes.cs"> <Compile Include="..\Discord.Net\Enums\ChannelTypes.cs">
<Link>Enums\ChannelTypes.cs</Link> <Link>Enums\ChannelTypes.cs</Link>
</Compile> </Compile>


+ 113
- 42
src/Discord.Net/DiscordClient.cs View File

@@ -6,19 +6,22 @@ using Discord.WebSockets.Data;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using VoiceWebSocket = Discord.WebSockets.Voice.VoiceWebSocket;


namespace Discord namespace Discord
{ {
/// <summary> Provides a connection to the DiscordApp service. </summary> /// <summary> Provides a connection to the DiscordApp service. </summary>
public partial class DiscordClient : DiscordBaseClient
public partial class DiscordClient : DiscordSimpleClient
{ {
protected readonly DiscordAPIClient _api; protected readonly DiscordAPIClient _api;
private readonly Random _rand; private readonly Random _rand;
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly ConcurrentQueue<Message> _pendingMessages; private readonly ConcurrentQueue<Message> _pendingMessages;
private readonly ConcurrentDictionary<string, DiscordSimpleClient> _voiceClients;


/// <summary> Returns the current logged-in user. </summary> /// <summary> Returns the current logged-in user. </summary>
public User CurrentUser => _currentUser; public User CurrentUser => _currentUser;
@@ -52,6 +55,8 @@ namespace Discord
_api = new DiscordAPIClient(_config.LogLevel, _config.APITimeout); _api = new DiscordAPIClient(_config.LogLevel, _config.APITimeout);
if (_config.UseMessageQueue) if (_config.UseMessageQueue)
_pendingMessages = new ConcurrentQueue<Message>(); _pendingMessages = new ConcurrentQueue<Message>();
if (_config.EnableVoiceMultiserver)
_voiceClients = new ConcurrentDictionary<string, DiscordSimpleClient>();


object cacheLock = new object(); object cacheLock = new object();
_channels = new Channels(this, cacheLock); _channels = new Channels(this, cacheLock);
@@ -61,38 +66,20 @@ namespace Discord
_servers = new Servers(this, cacheLock); _servers = new Servers(this, cacheLock);
_users = new Users(this, cacheLock); _users = new Users(this, cacheLock);


if (Config.VoiceMode != DiscordVoiceMode.Disabled)
this.Connected += (s,e) => _api.CancelToken = CancelToken;

VoiceDisconnected += (s, e) =>
{ {
this.VoiceDisconnected += (s, e) =>
{
foreach (var member in _members)
{
if (member.IsSpeaking)
{
member.IsSpeaking = false;
RaiseUserIsSpeaking(member, false);
}
}
};
_voiceSocket.IsSpeaking += (s, e) =>
foreach (var member in _members)
{ {
if (_voiceSocket.State == WebSocketState.Connected)
if (member.ServerId == e.ServerId && member.IsSpeaking)
{ {
var member = _members[e.UserId, _voiceSocket.CurrentServerId];
bool value = e.IsSpeaking;
if (member.IsSpeaking != value)
{
member.IsSpeaking = value;
RaiseUserIsSpeaking(member, value);
if (Config.TrackActivity)
member.UpdateActivity();
}
member.IsSpeaking = false;
RaiseUserIsSpeaking(member, false);
} }
};
}
}
};


this.Connected += (s,e) => _api.CancelToken = CancelToken;
if (_config.LogLevel >= LogMessageSeverity.Verbose) if (_config.LogLevel >= LogMessageSeverity.Verbose)
{ {
bool isDebug = _config.LogLevel >= LogMessageSeverity.Debug; bool isDebug = _config.LogLevel >= LogMessageSeverity.Debug;
@@ -207,6 +194,27 @@ namespace Discord
#endif #endif
} }


internal override VoiceWebSocket CreateVoiceSocket()
{
var socket = base.CreateVoiceSocket();
socket.IsSpeaking += (s, e) =>
{
if (_voiceSocket.State == WebSocketState.Connected)
{
var member = _members[e.UserId, socket.CurrentServerId];
bool value = e.IsSpeaking;
if (member.IsSpeaking != value)
{
member.IsSpeaking = value;
RaiseUserIsSpeaking(member, value);
if (_config.TrackActivity)
member.UpdateActivity();
}
}
};
return socket;
}

/// <summary> Connects to the Discord server with the provided email and password. </summary> /// <summary> Connects to the Discord server with the provided email and password. </summary>
/// <returns> Returns a token for future connections. </returns> /// <returns> Returns a token for future connections. </returns>
public new async Task<string> Connect(string email, string password) public new async Task<string> Connect(string email, string password)
@@ -249,6 +257,18 @@ namespace Discord
{ {
await base.Cleanup().ConfigureAwait(false); await base.Cleanup().ConfigureAwait(false);


if (_config.VoiceMode != DiscordVoiceMode.Disabled)
{
if (_config.EnableVoiceMultiserver)
{
var tasks = _voiceClients
.Select(x => x.Value.Disconnect())
.ToArray();
_voiceClients.Clear();
await Task.WhenAll(tasks).ConfigureAwait(false);
}
}

if (_config.UseMessageQueue) if (_config.UseMessageQueue)
{ {
Message ignored; Message ignored;
@@ -624,20 +644,6 @@ namespace Discord
} }
} }
break; break;
case "VOICE_SERVER_UPDATE":
{
var data = e.Payload.ToObject<VoiceServerUpdateEvent>(_serializer);
if (data.GuildId == _voiceSocket.CurrentServerId)
{
var server = _servers[data.GuildId];
if (_config.VoiceMode != DiscordVoiceMode.Disabled)
{
_voiceSocket.Host = "wss://" + data.Endpoint.Split(':')[0];
await _voiceSocket.Login(CurrentUserId, _dataSocket.SessionId, data.Token, CancelToken).ConfigureAwait(false);
}
}
}
break;


//Settings //Settings
case "USER_UPDATE": case "USER_UPDATE":
@@ -657,11 +663,76 @@ namespace Discord
} }
break; break;


//Internal (handled in DiscordSimpleClient)
case "VOICE_SERVER_UPDATE":
break;

//Others //Others
default: default:
RaiseOnLog(LogMessageSeverity.Warning, LogMessageSource.DataWebSocket, $"Unknown message type: {e.Type}"); RaiseOnLog(LogMessageSeverity.Warning, LogMessageSource.DataWebSocket, $"Unknown message type: {e.Type}");
break; break;
} }
} }

public IDiscordVoiceClient GetVoiceClient(string serverId)
{
if (serverId == null) throw new ArgumentNullException(nameof(serverId));

if (!_config.EnableVoiceMultiserver)
{
if (serverId == _voiceServerId)
return this;
else
return null;
}

DiscordSimpleClient client;
if (_voiceClients.TryGetValue(serverId, out client))
return client;
else
return null;
}
private async Task<IDiscordVoiceClient> CreateVoiceClient(string serverId)
{
if (!_config.EnableVoiceMultiserver)
{
_voiceServerId = serverId;
return this;
}

var client = _voiceClients.GetOrAdd(serverId, _ =>
{
var config = _config.Clone();
config.LogLevel = (LogMessageSeverity)Math.Min((int)_config.LogLevel, (int)LogMessageSeverity.Warning);
config.EnableVoiceMultiserver = false;
return new DiscordSimpleClient(config, serverId);
});
await client.Connect(_gateway, _token).ConfigureAwait(false);
return client;
}

public Task JoinVoiceServer(Channel channel)
=> JoinVoiceServer(channel?.ServerId, channel?.Id);
public Task JoinVoiceServer(Server server, string channelId)
=> JoinVoiceServer(server?.Id, channelId);
public async Task JoinVoiceServer(string serverId, string channelId)
{
CheckReady(); //checkVoice is done inside the voice client
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
if (channelId == null) throw new ArgumentNullException(nameof(channelId));

var client = await CreateVoiceClient(serverId).ConfigureAwait(false);
await client.JoinChannel(channelId).ConfigureAwait(false);
}

async Task LeaveVoiceServer(string serverId)
{
CheckReady(); //checkVoice is done inside the voice client
if (serverId == null) throw new ArgumentNullException(nameof(serverId));

DiscordSimpleClient client;
if (_voiceClients.TryRemove(serverId, out client))
await client.Disconnect();
}
} }
} }

+ 9
- 2
src/Discord.Net/DiscordClientConfig.cs View File

@@ -11,7 +11,7 @@ namespace Discord
Both = Outgoing | Incoming Both = Outgoing | Incoming
} }


public class DiscordClientConfig
public sealed class DiscordClientConfig
{ {
/// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary> /// <summary> Specifies the minimum log level severity that will be sent to the LogMessage event. Warning: setting this to debug will really hurt performance but should help investigate any internal issues. </summary>
public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } } public LogMessageSeverity LogLevel { get { return _logLevel; } set { SetValue(ref _logLevel, value); } }
@@ -72,5 +72,12 @@ namespace Discord
throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created.");
storage = value; storage = value;
} }
}

public DiscordClientConfig Clone()
{
var config = this.MemberwiseClone() as DiscordClientConfig;
config._isLocked = false;
return config;
}
}
} }

src/Discord.Net/DiscordBaseClient.Events.cs → src/Discord.Net/DiscordSimpleClient.Events.cs View File

@@ -32,6 +32,16 @@ namespace Discord
Error = error; Error = error;
} }
} }
public class VoiceDisconnectedEventArgs : DisconnectedEventArgs
{
public readonly string ServerId;

internal VoiceDisconnectedEventArgs(string serverId, DisconnectedEventArgs e)
: base(e.WasUnexpected, e.Error)
{
ServerId = serverId;
}
}
public sealed class LogMessageEventArgs : EventArgs public sealed class LogMessageEventArgs : EventArgs
{ {
public LogMessageSeverity Severity { get; } public LogMessageSeverity Severity { get; }
@@ -63,7 +73,7 @@ namespace Discord
} }
} }


public abstract partial class DiscordBaseClient
public partial class DiscordSimpleClient
{ {
public event EventHandler Connected; public event EventHandler Connected;
private void RaiseConnected() private void RaiseConnected()
@@ -90,11 +100,11 @@ namespace Discord
if (VoiceConnected != null) if (VoiceConnected != null)
RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty));
} }
public event EventHandler<DisconnectedEventArgs> VoiceDisconnected;
private void RaiseVoiceDisconnected(DisconnectedEventArgs e)
public event EventHandler<VoiceDisconnectedEventArgs> VoiceDisconnected;
private void RaiseVoiceDisconnected(string serverId, DisconnectedEventArgs e)
{ {
if (VoiceDisconnected != null) if (VoiceDisconnected != null)
RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, e));
RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e)));
} }


public event EventHandler<VoicePacketEventArgs> OnVoicePacket; public event EventHandler<VoicePacketEventArgs> OnVoicePacket;

src/Discord.Net/DiscordBaseClient.Voice.cs → src/Discord.Net/DiscordSimpleClient.Voice.cs View File

@@ -6,47 +6,53 @@ using System.Threading.Tasks;


namespace Discord namespace Discord
{ {
public partial class DiscordBaseClient
public interface IDiscordVoiceClient
{ {
public Task JoinVoiceServer(Channel channel)
=> JoinVoiceServer(channel?.ServerId, channel?.Id);
public Task JoinVoiceServer(Server server, string channelId)
=> JoinVoiceServer(server?.Id, channelId);
public async Task JoinVoiceServer(string serverId, string channelId)
Task JoinChannel(string channelId);
Task Disconnect();

void SendVoicePCM(byte[] data, int count);
void ClearVoicePCM();

Task WaitVoice();
}

public partial class DiscordSimpleClient : IDiscordVoiceClient
{
async Task IDiscordVoiceClient.JoinChannel(string channelId)
{ {
CheckReady(checkVoice: true); CheckReady(checkVoice: true);
if (serverId == null) throw new ArgumentNullException(nameof(serverId));
if (channelId == null) throw new ArgumentNullException(nameof(channelId)); if (channelId == null) throw new ArgumentNullException(nameof(channelId));

await LeaveVoiceServer().ConfigureAwait(false);
_voiceSocket.SetChannel(serverId, channelId);
_dataSocket.SendJoinVoice(serverId, channelId);
await ((IDiscordVoiceClient)this).Disconnect().ConfigureAwait(false);
_voiceSocket.SetChannel(_voiceServerId, channelId);
_dataSocket.SendJoinVoice(_voiceServerId, channelId);


CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationTokenSource tokenSource = new CancellationTokenSource();
try try
{ {
await Task.Run(() => _voiceSocket.WaitForConnection(tokenSource.Token))
await Task.Run(() => _voiceSocket.WaitForConnection(tokenSource.Token))
.Timeout(_config.ConnectionTimeout, tokenSource) .Timeout(_config.ConnectionTimeout, tokenSource)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch (TimeoutException) catch (TimeoutException)
{ {
tokenSource.Cancel(); tokenSource.Cancel();
await LeaveVoiceServer().ConfigureAwait(false);
await ((IDiscordVoiceClient)this).Disconnect().ConfigureAwait(false);
throw; throw;
} }
} }
public async Task LeaveVoiceServer()

async Task IDiscordVoiceClient.Disconnect()
{ {
CheckReady(checkVoice: true); CheckReady(checkVoice: true);


if (_voiceSocket.State != WebSocketState.Disconnected) if (_voiceSocket.State != WebSocketState.Disconnected)
{ {
var serverId = _voiceSocket.CurrentServerId;
if (serverId != null)
if (_voiceSocket.CurrentServerId != null)
{ {
await _voiceSocket.Disconnect().ConfigureAwait(false); await _voiceSocket.Disconnect().ConfigureAwait(false);
_dataSocket.SendLeaveVoice(serverId);
_dataSocket.SendLeaveVoice(_voiceSocket.CurrentServerId);
} }
} }
} }
@@ -54,7 +60,7 @@ namespace Discord
/// <summary> Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. </summary> /// <summary> Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. </summary>
/// <param name="data">PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. </param> /// <param name="data">PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. </param>
/// <param name="count">Number of bytes in this frame. </param> /// <param name="count">Number of bytes in this frame. </param>
public void SendVoicePCM(byte[] data, int count)
void IDiscordVoiceClient.SendVoicePCM(byte[] data, int count)
{ {
CheckReady(checkVoice: true); CheckReady(checkVoice: true);
if (data == null) throw new ArgumentException(nameof(data)); if (data == null) throw new ArgumentException(nameof(data));
@@ -64,7 +70,7 @@ namespace Discord
_voiceSocket.SendPCMFrames(data, count); _voiceSocket.SendPCMFrames(data, count);
} }
/// <summary> Clears the PCM buffer. </summary> /// <summary> Clears the PCM buffer. </summary>
public void ClearVoicePCM()
void IDiscordVoiceClient.ClearVoicePCM()
{ {
CheckReady(checkVoice: true); CheckReady(checkVoice: true);


@@ -72,7 +78,7 @@ namespace Discord
} }


/// <summary> Returns a task that completes once the voice output buffer is empty. </summary> /// <summary> Returns a task that completes once the voice output buffer is empty. </summary>
public async Task WaitVoice()
async Task IDiscordVoiceClient.WaitVoice()
{ {
CheckReady(checkVoice: true); CheckReady(checkVoice: true);



src/Discord.Net/DiscordBaseClient.cs → src/Discord.Net/DiscordSimpleClient.cs View File

@@ -1,10 +1,6 @@
using Discord.API;
using Discord.Collections;
using Discord.Helpers;
using Discord.Helpers;
using Discord.WebSockets.Data; using Discord.WebSockets.Data;
using System; using System;
using System.Collections.Concurrent;
using System.Net;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -21,18 +17,24 @@ namespace Discord
} }


/// <summary> Provides a barebones connection to the Discord service </summary> /// <summary> Provides a barebones connection to the Discord service </summary>
public partial class DiscordBaseClient
public partial class DiscordSimpleClient
{ {
internal readonly DataWebSocket _dataSocket; internal readonly DataWebSocket _dataSocket;
internal readonly VoiceWebSocket _voiceSocket; internal readonly VoiceWebSocket _voiceSocket;
protected readonly ManualResetEvent _disconnectedEvent; protected readonly ManualResetEvent _disconnectedEvent;
protected readonly ManualResetEventSlim _connectedEvent; protected readonly ManualResetEventSlim _connectedEvent;
protected readonly bool _enableVoice;
protected string _gateway, _token;
protected string _voiceServerId;
private Task _runTask; private Task _runTask;
private string _gateway, _token;


protected ExceptionDispatchInfo _disconnectReason; protected ExceptionDispatchInfo _disconnectReason;
private bool _wasDisconnectUnexpected; private bool _wasDisconnectUnexpected;


/// <summary> Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. </summary>
public DiscordClientConfig Config => _config;
protected readonly DiscordClientConfig _config;

/// <summary> Returns the id of the current logged-in user. </summary> /// <summary> Returns the id of the current logged-in user. </summary>
public string CurrentUserId => _currentUserId; public string CurrentUserId => _currentUserId;
private string _currentUserId; private string _currentUserId;
@@ -43,64 +45,78 @@ namespace Discord
public DiscordClientState State => (DiscordClientState)_state; public DiscordClientState State => (DiscordClientState)_state;
private int _state; private int _state;


/// <summary> Returns the configuration object used to make this client. Note that this object cannot be edited directly - to change the configuration of this client, use the DiscordClient(DiscordClientConfig config) constructor. </summary>
public DiscordClientConfig Config => _config;
protected readonly DiscordClientConfig _config;

public CancellationToken CancelToken => _cancelToken; public CancellationToken CancelToken => _cancelToken;
private CancellationTokenSource _cancelTokenSource; private CancellationTokenSource _cancelTokenSource;
private CancellationToken _cancelToken; private CancellationToken _cancelToken;


/// <summary> Initializes a new instance of the DiscordClient class. </summary> /// <summary> Initializes a new instance of the DiscordClient class. </summary>
public DiscordBaseClient(DiscordClientConfig config = null)
public DiscordSimpleClient(DiscordClientConfig config = null)
{ {
_config = config ?? new DiscordClientConfig(); _config = config ?? new DiscordClientConfig();
_config.Lock(); _config.Lock();


_enableVoice = config.VoiceMode != DiscordVoiceMode.Disabled && !config.EnableVoiceMultiserver;

_state = (int)DiscordClientState.Disconnected; _state = (int)DiscordClientState.Disconnected;
_cancelToken = new CancellationToken(true); _cancelToken = new CancellationToken(true);
_disconnectedEvent = new ManualResetEvent(true); _disconnectedEvent = new ManualResetEvent(true);
_connectedEvent = new ManualResetEventSlim(false); _connectedEvent = new ManualResetEventSlim(false);


_dataSocket = new DataWebSocket(this);
_dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); };
_dataSocket.Disconnected += async (s, e) =>
_dataSocket = CreateDataSocket();
if (_enableVoice)
_voiceSocket = CreateVoiceSocket();
}
internal DiscordSimpleClient(DiscordClientConfig config = null, string serverId = null)
: this(config)
{
_voiceServerId = serverId;
}

internal virtual DataWebSocket CreateDataSocket()
{
var socket = new DataWebSocket(this);
socket.Connected += (s, e) =>
{
if (_state == (int)DiscordClientState.Connecting)
CompleteConnect(); }
;
socket.Disconnected += async (s, e) =>
{ {
RaiseDisconnected(e); RaiseDisconnected(e);
if (e.WasUnexpected) if (e.WasUnexpected)
await _dataSocket.Reconnect(_token);
await socket.Reconnect(_token);
}; };
if (Config.VoiceMode != DiscordVoiceMode.Disabled)
socket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.DataWebSocket, e.Message);
if (_config.LogLevel >= LogMessageSeverity.Info)
{ {
_voiceSocket = new VoiceWebSocket(this);
_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
_voiceSocket.Disconnected += async (s, e) =>
{
RaiseVoiceDisconnected(e);
if (e.WasUnexpected)
await _voiceSocket.Reconnect();
};
socket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected");
socket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected");
} }


_dataSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.DataWebSocket, e.Message);
if (_config.VoiceMode != DiscordVoiceMode.Disabled)
_voiceSocket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message);
socket.ReceivedEvent += (s, e) => OnReceivedEvent(e);
return socket;
}
internal virtual VoiceWebSocket CreateVoiceSocket()
{
var socket = new VoiceWebSocket(this);
socket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message);
socket.Connected += (s, e) => RaiseVoiceConnected();
socket.Disconnected += async (s, e) =>
{
RaiseVoiceDisconnected(socket.CurrentServerId, e);
if (e.WasUnexpected)
await socket.Reconnect();
};
if (_config.LogLevel >= LogMessageSeverity.Info) if (_config.LogLevel >= LogMessageSeverity.Info)
{ {
_dataSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Connected");
_dataSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.DataWebSocket, "Disconnected");
if (_config.VoiceMode != DiscordVoiceMode.Disabled)
{
_voiceSocket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Connected");
_voiceSocket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Disconnected");
}
socket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Connected");
socket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Disconnected");
} }

_dataSocket.ReceivedEvent += (s, e) => OnReceivedEvent(e);
return socket;
} }


//Connection //Connection
protected async Task<string> Connect(string gateway, string token)
public async Task<string> Connect(string gateway, string token)
{ {
try try
{ {
@@ -132,7 +148,6 @@ namespace Discord
} }


//_state = (int)DiscordClientState.Connected; //_state = (int)DiscordClientState.Connected;
_token = token;
return token; return token;
} }
catch catch
@@ -222,7 +237,7 @@ namespace Discord
protected virtual async Task Cleanup() protected virtual async Task Cleanup()
{ {
await _dataSocket.Disconnect().ConfigureAwait(false); await _dataSocket.Disconnect().ConfigureAwait(false);
if (_config.VoiceMode != DiscordVoiceMode.Disabled)
if (_enableVoice)
await _voiceSocket.Disconnect().ConfigureAwait(false); await _voiceSocket.Disconnect().ConfigureAwait(false);
_currentUserId = null; _currentUserId = null;
@@ -249,7 +264,7 @@ namespace Discord
throw new InvalidOperationException("The client is connecting."); throw new InvalidOperationException("The client is connecting.");
} }
if (checkVoice && _config.VoiceMode == DiscordVoiceMode.Disabled)
if (checkVoice && !_enableVoice)
throw new InvalidOperationException("Voice is not enabled for this client."); throw new InvalidOperationException("Voice is not enabled for this client.");
} }
protected void RaiseEvent(string name, Action action) protected void RaiseEvent(string name, Action action)
@@ -264,8 +279,24 @@ namespace Discord


internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e) internal virtual Task OnReceivedEvent(WebSocketEventEventArgs e)
{ {
if (e.Type == "READY")
_currentUserId = e.Payload["user"].Value<string>("id");
switch (e.Type)
{
case "READY":
_currentUserId = e.Payload["user"].Value<string>("id");
break;
case "VOICE_SERVER_UPDATE":
{
string guildId = e.Payload.Value<string>("guild_id");
if (_enableVoice && guildId == _voiceSocket.CurrentServerId)
{
string token = e.Payload.Value<string>("token");
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0];
return _voiceSocket.Login(_currentUserId, _dataSocket.SessionId, token, CancelToken);
}
}
break;
}
return TaskHelper.CompletedTask; return TaskHelper.CompletedTask;
} }
} }

+ 1
- 1
src/Discord.Net/WebSockets/Data/DataWebSocket.cs View File

@@ -12,7 +12,7 @@ namespace Discord.WebSockets.Data
public string SessionId => _sessionId; public string SessionId => _sessionId;
private string _sessionId; private string _sessionId;


public DataWebSocket(DiscordBaseClient client)
public DataWebSocket(DiscordSimpleClient client)
: base(client) : base(client)
{ {
} }


+ 23
- 15
src/Discord.Net/WebSockets/Voice/VoiceWebSocket.cs View File

@@ -5,6 +5,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -45,7 +46,7 @@ namespace Discord.WebSockets.Voice
public string CurrentServerId => _serverId; public string CurrentServerId => _serverId;
public string CurrentChannelId => _channelId; public string CurrentChannelId => _channelId;


public VoiceWebSocket(DiscordBaseClient client)
public VoiceWebSocket(DiscordSimpleClient client)
: base(client) : base(client)
{ {
_rand = new Random(); _rand = new Random();
@@ -121,25 +122,32 @@ namespace Discord.WebSockets.Voice
msg.Payload.UserId = _userId; msg.Payload.UserId = _userId;
QueueMessage(msg); QueueMessage(msg);


List<Task> tasks = new List<Task>();
if ((_client.Config.VoiceMode & DiscordVoiceMode.Outgoing) != 0)
{
#if USE_THREAD #if USE_THREAD
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken)));
_sendThread.Start();
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken)));
_receiveThread.Start();
#if !DNXCORE50
return new Task[] { WatcherAsync() }.Concat(base.Run()).ToArray();
_sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_cancelToken)));
_sendThread.Start();
#else #else
return base.Run();
tasks.Add(SendVoiceAsync());
#endif #endif
#else //!USE_THREAD
return new Task[] { Task.WhenAll(
ReceiveVoiceAsync(),
SendVoiceAsync(),
#if !DNXCORE50
WatcherAsync()
}
if ((_client.Config.VoiceMode & DiscordVoiceMode.Incoming) != 0)
{
#if USE_THREAD
_receiveThread = new Thread(new ThreadStart(() => ReceiveVoiceAsync(_cancelToken)));
_receiveThread.Start();
#else
tasks.Add(ReceiveVoiceAsync());
#endif #endif
)}.Concat(base.Run()).ToArray();
}

#if !DNXCORE50
tasks.Add(WatcherAsync());
#endif #endif
tasks.AddRange(base.Run());
return tasks.ToArray();
} }
protected override Task Cleanup() protected override Task Cleanup()
{ {


+ 2
- 2
src/Discord.Net/WebSockets/WebSocket.cs View File

@@ -35,7 +35,7 @@ namespace Discord.WebSockets
internal abstract partial class WebSocket internal abstract partial class WebSocket
{ {
protected readonly IWebSocketEngine _engine; protected readonly IWebSocketEngine _engine;
protected readonly DiscordBaseClient _client;
protected readonly DiscordSimpleClient _client;
protected readonly LogMessageSeverity _logLevel; protected readonly LogMessageSeverity _logLevel;
protected readonly ManualResetEventSlim _connectedEvent; protected readonly ManualResetEventSlim _connectedEvent;


@@ -57,7 +57,7 @@ namespace Discord.WebSockets
public WebSocketState State => (WebSocketState)_state; public WebSocketState State => (WebSocketState)_state;
protected int _state; protected int _state;


public WebSocket(DiscordBaseClient client)
public WebSocket(DiscordSimpleClient client)
{ {
_client = client; _client = client;
_logLevel = client.Config.LogLevel; _logLevel = client.Config.LogLevel;


Loading…
Cancel
Save