diff --git a/DiscordRPC/DiscordRpcClient.cs b/DiscordRPC/DiscordRpcClient.cs index 959aff23..49eb6d45 100644 --- a/DiscordRPC/DiscordRpcClient.cs +++ b/DiscordRPC/DiscordRpcClient.cs @@ -18,6 +18,20 @@ public sealed class DiscordRpcClient : IDisposable { #region Properties + /// + /// Maximal amount of tries before aborting connection. If -1, then limit isn't present. + /// + /// 15 should be optimal value (~70s) + public int MaxConnectionTries + { + get { return _maxConnectionTries; } + set + { + _maxConnectionTries = value; + if (connection != null) connection.MaxConnectionTries = value; + } + } + private int _maxConnectionTries; /// /// Gets a value indicating if the client has registered a URI Scheme. If this is false, Join events will fail. @@ -41,7 +55,7 @@ public sealed class DiscordRpcClient : IDisposable public int ProcessID { get; private set; } /// - /// The maximum size of the message queue received from Discord. + /// The maximum size of the message queue received from Discord. /// public int MaxQueueSize { get; private set; } @@ -197,6 +211,11 @@ public bool ShutdownOnly /// The RPC Connection has sent a message. Called before any other event and executed from the RPC Thread. /// public event OnRpcMessageEvent OnRpcMessage; + + /// + /// Too many failed connection tries. You must reinitialize the client. + /// + public event OnTooManyConnectionTriesEvent OnTooManyConnectionTries; #endregion #region Initialization @@ -215,7 +234,11 @@ public DiscordRpcClient(string applicationID) : this(applicationID, -1) { } /// The logger used to report messages. If null, then a will be created and logs will be ignored. /// Should events be automatically invoked from the RPC Thread as they arrive from discord? /// The pipe client to use and communicate to discord through. If null, the default will be used. - public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = null, bool autoEvents = true, INamedPipeClient client = null) + /// + /// The maximum number of connection attempts before terminating. + /// Use -1 to disable the limit entirely. A value of 15 is typically optimal (~70 seconds). + /// + public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = null, bool autoEvents = true, INamedPipeClient client = null, int maxConnectionTries = -1) { //Make sure appID is NOT null. if (string.IsNullOrEmpty(applicationID)) @@ -228,6 +251,7 @@ public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = nu HasRegisteredUriScheme = false; AutoEvents = autoEvents; SkipIdenticalPresence = true; + _maxConnectionTries = maxConnectionTries; //Prepare the logger _logger = logger ?? new NullLogger(); @@ -236,7 +260,8 @@ public DiscordRpcClient(string applicationID, int pipe = -1, ILogger logger = nu connection = new RpcConnection(ApplicationID, ProcessID, TargetPipe, client ?? new ManagedNamedPipeClient(), autoEvents ? 0 : 128U) { ShutdownOnly = _shutdownOnly, - Logger = _logger + Logger = _logger, + MaxConnectionTries = _maxConnectionTries }; //Subscribe to its event @@ -405,6 +430,13 @@ private void ProcessMessage(IMessage message) if (OnConnectionFailed != null) OnConnectionFailed.Invoke(this, message as ConnectionFailedMessage); break; + + case MessageType.TooManyConnectionTries: + if (OnTooManyConnectionTries != null) + OnTooManyConnectionTries.Invoke(this, message as TooManyConnectionTriesMessage); + + Deinitialize(); + break; //We got a message we dont know what to do with. default: diff --git a/DiscordRPC/Events.cs b/DiscordRPC/Events.cs index 12df7cdf..2a75bb5a 100644 --- a/DiscordRPC/Events.cs +++ b/DiscordRPC/Events.cs @@ -85,6 +85,13 @@ namespace DiscordRPC.Events /// The arguments supplied with the event public delegate void OnConnectionFailedEvent(object sender, ConnectionFailedMessage args); + /// + /// Too many failed connection tries. You must reinitialize the client. + /// + /// The Discord client handler that sent this event + /// The arguments supplied with the event + public delegate void OnTooManyConnectionTriesEvent(object sender, TooManyConnectionTriesMessage args); + /// /// A RPC Message is received. diff --git a/DiscordRPC/Message/MessageType.cs b/DiscordRPC/Message/MessageType.cs index 223291ee..3e1f1f1e 100644 --- a/DiscordRPC/Message/MessageType.cs +++ b/DiscordRPC/Message/MessageType.cs @@ -35,7 +35,7 @@ public enum MessageType /// The Discord Client has unsubscribed from an event. /// Unsubscribe, - + /// /// The Discord Client wishes for this process to join a game. /// @@ -59,6 +59,10 @@ public enum MessageType /// /// Failed to establish any connection with discord. Discord is potentially not running? /// - ConnectionFailed + ConnectionFailed, + /// + /// Too many failed connection tries. You must reinitialize the client. + /// + TooManyConnectionTries } } diff --git a/DiscordRPC/Message/TooManyConnectionTriesMessage.cs b/DiscordRPC/Message/TooManyConnectionTriesMessage.cs new file mode 100644 index 00000000..32a76d7b --- /dev/null +++ b/DiscordRPC/Message/TooManyConnectionTriesMessage.cs @@ -0,0 +1,13 @@ +namespace DiscordRPC.Message +{ + /// + /// Too many failed connection tries. You must reinitialize the client. + /// + public class TooManyConnectionTriesMessage : IMessage + { + /// + /// The type of message received from discord + /// + public override MessageType Type { get { return MessageType.TooManyConnectionTries; } } + } +} diff --git a/DiscordRPC/RPC/RpcConnection.cs b/DiscordRPC/RPC/RpcConnection.cs index 52099049..51d28f7a 100644 --- a/DiscordRPC/RPC/RpcConnection.cs +++ b/DiscordRPC/RPC/RpcConnection.cs @@ -52,6 +52,9 @@ public ILogger Logger } private ILogger _logger; + + public int MaxConnectionTries { get; set; } + /// /// Called when a message is received from the RPC and is about to be enqueued. This is cross-thread and will execute on the RPC thread. /// @@ -118,6 +121,7 @@ public RpcState State private AutoResetEvent queueUpdatedEvent = new AutoResetEvent(false); private BackoffDelay delay; //The backoff delay before reconnecting. + private int tries; //number of unsuccessful connections in row #endregion /// @@ -129,13 +133,15 @@ public RpcState State /// The pipe client we shall use. /// The maximum size of the out queue /// The maximum size of the in queue - public RpcConnection(string applicationID, int processID, int targetPipe, INamedPipeClient client, uint maxRxQueueSize = 128, uint maxRtQueueSize = 512) + /// The maximum amount of tries taken before terminating + public RpcConnection(string applicationID, int processID, int targetPipe, INamedPipeClient client, uint maxRxQueueSize = 128, uint maxRtQueueSize = 512, int maxConnectionTries = -1) { this.applicationID = applicationID; this.processID = processID; this.targetPipe = targetPipe; - this.namedPipe = client; - this.ShutdownOnly = true; + namedPipe = client; + ShutdownOnly = true; + MaxConnectionTries = maxConnectionTries; //Assign a default logger Logger = new ConsoleLogger(); @@ -316,6 +322,7 @@ private void MainLoop() //Attempt to establish a handshake EstablishHandshake(); Logger.Trace("Connection Established. Starting reading loop..."); + tries = 0; //Continously iterate, waiting for the frame //We want to only stop reading if the inside tells us (mainloop), if we are aborting (abort) or the pipe disconnects @@ -431,9 +438,18 @@ private void MainLoop() // so we are going to wait a bit before doing it again long sleep = delay.NextDelay(); + tries++; + Logger.Trace("Waiting {0}ms before attempting to connect again", sleep); Thread.Sleep(delay.NextDelay()); } + if (tries >= MaxConnectionTries && MaxConnectionTries != -1) + { + Logger.Error("Terminating attempts to connect, exceeded amount of tries"); + + EnqueueMessage(new TooManyConnectionTriesMessage()); + break; + } } //catch(InvalidPipeException e) //{