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
This commit is contained in:
BruceChen 2026-04-06 19:23:34 +08:00
parent e6ea29c914
commit 0f32771075

View file

@ -54,6 +54,8 @@ namespace MinecraftClient
private static Tuple<Thread, CancellationTokenSource>? 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,8 +894,16 @@ namespace MinecraftClient
/// <param name="keepAccountAndServerSettings">Optional, keep account and server settings</param>
public static void Restart(int delaySeconds = 0, bool keepAccountAndServerSettings = false)
{
lock (_restartLock)
{
if (_restartThread is not null && _restartThread.IsAlive
&& _restartThread != Thread.CurrentThread)
return;
ConsoleIO.Backend?.StopReadThread();
new Thread(new ThreadStart(delegate
var thread = new Thread(new ThreadStart(delegate
{
try
{
if (client is not null) { client.Disconnect(); ConsoleIO.Reset(); }
if (offlinePrompt is not null)
@ -910,7 +920,19 @@ namespace MinecraftClient
ConsoleIO.WriteLine(Translations.mcc_restart);
ReloadSettings(keepAccountAndServerSettings);
InitializeClient();
})).Start();
}
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
/// <param name="disconnectReason">If set, the error message will be processed by the AutoRelog bot</param>
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)