From d677ae842c3c6b44b734ea8d71a66052f8cf58d1 Mon Sep 17 00:00:00 2001 From: OpalSoPL <90208316+OpalSoPL@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:39:20 +0200 Subject: [PATCH 1/2] feat: terminate connection after exceeding set amount of tries --- DiscordRPC/DiscordRpcClient.cs | 38 +++++++- DiscordRPC/Events.cs | 7 ++ DiscordRPC/Message/MessageType.cs | 8 +- .../Message/TooManyConnectionTriesMessage.cs | 13 +++ DiscordRPC/RPC/RpcConnection.cs | 95 +++++++++++-------- 5 files changed, 117 insertions(+), 44 deletions(-) create mode 100644 DiscordRPC/Message/TooManyConnectionTriesMessage.cs diff --git a/DiscordRPC/DiscordRpcClient.cs b/DiscordRPC/DiscordRpcClient.cs index 1c58df9c..7ea0e3b6 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 / Spectate 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; } @@ -196,6 +210,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 OnTooMannyConnectionTriesEvent OnTooMannyConnectionTries; #endregion #region Initialization @@ -214,7 +233,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)) @@ -227,6 +250,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(); @@ -235,7 +259,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 @@ -404,6 +429,13 @@ private void ProcessMessage(IMessage message) if (OnConnectionFailed != null) OnConnectionFailed.Invoke(this, message as ConnectionFailedMessage); break; + + case MessageType.TooMannyConnectionTries: + if (OnTooMannyConnectionTries != null) + OnTooMannyConnectionTries.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 b28c7fec..3d98078d 100644 --- a/DiscordRPC/Events.cs +++ b/DiscordRPC/Events.cs @@ -84,6 +84,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 OnTooMannyConnectionTriesEvent(object sender, TooManyConnectionTriesMessage args); + /// /// A RPC Message is received. diff --git a/DiscordRPC/Message/MessageType.cs b/DiscordRPC/Message/MessageType.cs index 223291ee..42f2d146 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. + /// + TooMannyConnectionTries } } diff --git a/DiscordRPC/Message/TooManyConnectionTriesMessage.cs b/DiscordRPC/Message/TooManyConnectionTriesMessage.cs new file mode 100644 index 00000000..e9d6eb30 --- /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.TooMannyConnectionTries; } } + } +} diff --git a/DiscordRPC/RPC/RpcConnection.cs b/DiscordRPC/RPC/RpcConnection.cs index fd8ea6a4..19ca9da2 100644 --- a/DiscordRPC/RPC/RpcConnection.cs +++ b/DiscordRPC/RPC/RpcConnection.cs @@ -52,10 +52,12 @@ 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. - /// - public event OnRpcMessageEvent OnRpcMessage; + /// 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. + /// + public event OnRpcMessageEvent OnRpcMessage; #region States @@ -118,6 +120,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 /// @@ -127,26 +130,28 @@ public RpcState State /// The ID of the currently running process /// The target pipe to connect too /// 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 size of the out queue + /// The maximum size of the in queue + /// 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(); delay = new BackoffDelay(500, 60 * 1000); - _maxRtQueueSize = maxRtQueueSize; + _maxRtQueueSize = maxRtQueueSize; _rtqueue = new Queue((int)_maxRtQueueSize + 1); - _maxRxQueueSize = maxRxQueueSize; - _rxqueue = new Queue((int)_maxRxQueueSize + 1); - + _maxRxQueueSize = maxRxQueueSize; + _rxqueue = new Queue((int)_maxRxQueueSize + 1); + nonce = 0; } @@ -263,31 +268,31 @@ internal IMessage[] DequeueMessages() } } #endregion - + /// /// Main thread loop /// private void MainLoop() { - //initialize the pipe - Logger.Info("RPC Connection Started"); - if (Logger.Level <= LogLevel.Trace) - { - Logger.Trace("============================"); - Logger.Trace("Assembly: " + System.Reflection.Assembly.GetAssembly(typeof(RichPresence)).FullName); - Logger.Trace("Pipe: " + namedPipe.GetType().FullName); - Logger.Trace("Platform: " + Environment.OSVersion.ToString()); - Logger.Trace("applicationID: " + applicationID); - Logger.Trace("targetPipe: " + targetPipe); - Logger.Trace("POLL_RATE: " + POLL_RATE); - Logger.Trace("_maxRtQueueSize: " + _maxRtQueueSize); - Logger.Trace("_maxRxQueueSize: " + _maxRxQueueSize); - Logger.Trace("============================"); - } + //initialize the pipe + Logger.Info("RPC Connection Started"); + if (Logger.Level <= LogLevel.Trace) + { + Logger.Trace("============================"); + Logger.Trace("Assembly: " + System.Reflection.Assembly.GetAssembly(typeof(RichPresence)).FullName); + Logger.Trace("Pipe: " + namedPipe.GetType().FullName); + Logger.Trace("Platform: " + Environment.OSVersion.ToString()); + Logger.Trace("applicationID: " + applicationID); + Logger.Trace("targetPipe: " + targetPipe); + Logger.Trace("POLL_RATE: " + POLL_RATE); + Logger.Trace("_maxRtQueueSize: " + _maxRtQueueSize); + Logger.Trace("_maxRxQueueSize: " + _maxRxQueueSize); + Logger.Trace("============================"); + } - //Forever trying to connect unless the abort signal is sent - //Keep Alive Loop - while (!aborting && !shutdown) + //Forever trying to connect unless the abort signal is sent + //Keep Alive Loop + while (!aborting && !shutdown) { try { @@ -312,6 +317,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 @@ -341,19 +347,19 @@ private void MainLoop() break; //We have pinged, so we will flip it and respond back with pong - case Opcode.Ping: + case Opcode.Ping: Logger.Trace("PING"); frame.Opcode = Opcode.Pong; namedPipe.WriteFrame(frame); break; //We have ponged? I have no idea if Discord actually sends ping/pongs. - case Opcode.Pong: + case Opcode.Pong: Logger.Trace("PONG"); break; //A frame has been sent, we should deal with that - case Opcode.Frame: + case Opcode.Frame: if (shutdown) { //We are shutting down, so skip it @@ -370,21 +376,23 @@ private void MainLoop() //We have a frame, so we are going to process the payload and add it to the stack EventPayload response = null; - try { response = frame.GetObject(); } catch (Exception e) + try { response = frame.GetObject(); } + catch (Exception e) { Logger.Error("Failed to parse event! {0}", e.Message); Logger.Error("Data: {0}", frame.Message); } - try { if (response != null) ProcessFrame(response); } catch(Exception e) - { + try { if (response != null) ProcessFrame(response); } + catch (Exception e) + { Logger.Error("Failed to process event! {0}", e.Message); Logger.Error("Data: {0}", frame.Message); } break; - + default: case Opcode.Handshake: @@ -398,7 +406,7 @@ private void MainLoop() } if (!aborting && namedPipe.IsConnected) - { + { //Process the entire command queue we have left ProcessCommandQueue(); @@ -425,9 +433,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) //{ From 5c792f3f1b50dbf33c2dfa5cd366c8dd78cb139e Mon Sep 17 00:00:00 2001 From: OpalSoPL <90208316+OpalSoPL@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:46:06 +0200 Subject: [PATCH 2/2] fix: typo in tooManyConnectionTries --- DiscordRPC/DiscordRpcClient.cs | 8 ++++---- DiscordRPC/Events.cs | 2 +- DiscordRPC/Message/MessageType.cs | 2 +- DiscordRPC/Message/TooManyConnectionTriesMessage.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DiscordRPC/DiscordRpcClient.cs b/DiscordRPC/DiscordRpcClient.cs index 7ea0e3b6..d7a0d200 100644 --- a/DiscordRPC/DiscordRpcClient.cs +++ b/DiscordRPC/DiscordRpcClient.cs @@ -214,7 +214,7 @@ public bool ShutdownOnly /// /// Too many failed connection tries. You must reinitialize the client. /// - public event OnTooMannyConnectionTriesEvent OnTooMannyConnectionTries; + public event OnTooManyConnectionTriesEvent OnTooManyConnectionTries; #endregion #region Initialization @@ -430,9 +430,9 @@ private void ProcessMessage(IMessage message) OnConnectionFailed.Invoke(this, message as ConnectionFailedMessage); break; - case MessageType.TooMannyConnectionTries: - if (OnTooMannyConnectionTries != null) - OnTooMannyConnectionTries.Invoke(this, message as TooManyConnectionTriesMessage); + case MessageType.TooManyConnectionTries: + if (OnTooManyConnectionTries != null) + OnTooManyConnectionTries.Invoke(this, message as TooManyConnectionTriesMessage); Deinitialize(); break; diff --git a/DiscordRPC/Events.cs b/DiscordRPC/Events.cs index 3d98078d..7556d74d 100644 --- a/DiscordRPC/Events.cs +++ b/DiscordRPC/Events.cs @@ -89,7 +89,7 @@ namespace DiscordRPC.Events /// /// The Discord client handler that sent this event /// The arguments supplied with the event - public delegate void OnTooMannyConnectionTriesEvent(object sender, TooManyConnectionTriesMessage args); + public delegate void OnTooManyConnectionTriesEvent(object sender, TooManyConnectionTriesMessage args); /// diff --git a/DiscordRPC/Message/MessageType.cs b/DiscordRPC/Message/MessageType.cs index 42f2d146..3e1f1f1e 100644 --- a/DiscordRPC/Message/MessageType.cs +++ b/DiscordRPC/Message/MessageType.cs @@ -63,6 +63,6 @@ public enum MessageType /// /// Too many failed connection tries. You must reinitialize the client. /// - TooMannyConnectionTries + TooManyConnectionTries } } diff --git a/DiscordRPC/Message/TooManyConnectionTriesMessage.cs b/DiscordRPC/Message/TooManyConnectionTriesMessage.cs index e9d6eb30..32a76d7b 100644 --- a/DiscordRPC/Message/TooManyConnectionTriesMessage.cs +++ b/DiscordRPC/Message/TooManyConnectionTriesMessage.cs @@ -8,6 +8,6 @@ public class TooManyConnectionTriesMessage : IMessage /// /// The type of message received from discord /// - public override MessageType Type { get { return MessageType.TooMannyConnectionTries; } } + public override MessageType Type { get { return MessageType.TooManyConnectionTries; } } } }