From 0f32771075c2a4a8a6153e93ac5a3207b08538cf Mon Sep 17 00:00:00 2001 From: BruceChen Date: Mon, 6 Apr 2026 19:23:34 +0800 Subject: [PATCH] fix: prevent concurrent restart flood on rapid /reco (#3046) - Add reentrant guard to Program.Restart: track the active restart thread and silently drop concurrent calls from other threads while allowing the restart thread itself to chain a new restart (needed for AutoRelog retry flow). - Fix double AutoRelog.OnDisconnectStatic invocation in HandleFailure: the method was called once in the error-message branch and again in the interactive-mode branch for the same failure event, doubling the retry counter increment and potentially spawning duplicate restarts. Made-with: Cursor --- MinecraftClient/Program.cs | 65 ++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 58a42b9b..91c9ffb1 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -54,6 +54,8 @@ namespace MinecraftClient private static Tuple? offlinePrompt = null; private static IDisposable? _sentrySdk = null; private static bool useMcVersionOnce = false; + private static Thread? _restartThread = null; + private static readonly object _restartLock = new(); private static string settingsIniPath = "MinecraftClient.ini"; // [SENTRY] @@ -892,25 +894,45 @@ namespace MinecraftClient /// Optional, keep account and server settings public static void Restart(int delaySeconds = 0, bool keepAccountAndServerSettings = false) { - ConsoleIO.Backend?.StopReadThread(); - new Thread(new ThreadStart(delegate + lock (_restartLock) { - if (client is not null) { client.Disconnect(); ConsoleIO.Reset(); } - if (offlinePrompt is not null) + if (_restartThread is not null && _restartThread.IsAlive + && _restartThread != Thread.CurrentThread) + return; + + ConsoleIO.Backend?.StopReadThread(); + var thread = new Thread(new ThreadStart(delegate { - if (ConsoleIO.Backend is not null) - ConsoleIO.Backend.OnInputChange -= ConsoleIO.OfflineAutocompleteHandler; - offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); - } - if (delaySeconds > 0) - { - ConsoleIO.WriteLine(string.Format(Translations.mcc_restart_delay, delaySeconds)); - Thread.Sleep(delaySeconds * 1000); - } - ConsoleIO.WriteLine(Translations.mcc_restart); - ReloadSettings(keepAccountAndServerSettings); - InitializeClient(); - })).Start(); + try + { + if (client is not null) { client.Disconnect(); ConsoleIO.Reset(); } + if (offlinePrompt is not null) + { + if (ConsoleIO.Backend is not null) + ConsoleIO.Backend.OnInputChange -= ConsoleIO.OfflineAutocompleteHandler; + offlinePrompt.Item2.Cancel(); offlinePrompt.Item1.Join(); offlinePrompt = null; ConsoleIO.Reset(); + } + if (delaySeconds > 0) + { + ConsoleIO.WriteLine(string.Format(Translations.mcc_restart_delay, delaySeconds)); + Thread.Sleep(delaySeconds * 1000); + } + ConsoleIO.WriteLine(Translations.mcc_restart); + ReloadSettings(keepAccountAndServerSettings); + InitializeClient(); + } + finally + { + lock (_restartLock) + { + if (_restartThread == Thread.CurrentThread) + _restartThread = null; + } + } + })); + _restartThread = thread; + thread.Start(); + } } public static void DoExit(int exitcode = 0) @@ -951,6 +973,8 @@ namespace MinecraftClient /// If set, the error message will be processed by the AutoRelog bot public static void HandleFailure(string? errorMessage = null, bool versionError = false, ChatBot.DisconnectReason? disconnectReason = null) { + bool autoRelogHandled = false; + if (!string.IsNullOrEmpty(errorMessage)) { ConsoleIO.Reset(); @@ -967,8 +991,9 @@ namespace MinecraftClient if (disconnectReason.HasValue) { + autoRelogHandled = true; if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage)) - return; //AutoRelog is triggering a restart of the client + return; } } @@ -986,10 +1011,10 @@ namespace MinecraftClient } } - if (disconnectReason.HasValue) + if (!autoRelogHandled && disconnectReason.HasValue) { if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage!)) - return; //AutoRelog is triggering a restart of the client, don't turn on the offline prompt + return; } if (offlinePrompt is null)