From c33c17ba18840edeb8a7358d2222c92d1268626b Mon Sep 17 00:00:00 2001 From: Somersault <130008175+Somersault41@users.noreply.github.com> Date: Mon, 29 Jun 2026 05:36:13 +0300 Subject: [PATCH] Update McClient.cs --- MinecraftClient/McClient.cs | 112 +++++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 3aa23c40..aca76616 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -29,6 +29,14 @@ using static MinecraftClient.Settings; namespace MinecraftClient { + internal class AutoRelogReconnectRequestedException : Exception + { + public AutoRelogReconnectRequestedException() + : base("AutoRelog requested reconnect") + { + } + } + /// /// The main client class, used to connect to a Minecraft server. /// @@ -375,7 +383,7 @@ namespace MinecraftClient Log.Info(string.Format(Translations.mcc_reconnect, ReconnectionAttemptsLeft)); Thread.Sleep(5000); ReconnectionAttemptsLeft--; - Program.Restart(); + Program.Reconnect(); } else if (InternalConfig.InteractiveMode) { @@ -391,7 +399,7 @@ namespace MinecraftClient // Use the same "Connection has been lost" message that OnConnectionLost uses // for ConnectionLost, so it matches the default Kick_Messages. if (AutoRelog.OnDisconnectStatic(ChatBot.DisconnectReason.ConnectionLost, Translations.mcc_disconnect_lost)) - return; // AutoRelog is triggering a restart + throw new AutoRelogReconnectRequestedException(); // AutoRelog is triggering a restart // AutoRelog chose not to reconnect (e.g., message didn't match // kick messages and Ignore_Kick_Message is false, or retry limit reached) @@ -498,7 +506,7 @@ namespace MinecraftClient Log.Info($"Reconnecting... Attempts left: {ReconnectionAttemptsLeft}"); Thread.Sleep(5000); ReconnectionAttemptsLeft--; - Program.Restart(); + Program.Reconnect(); } else if (InternalConfig.InteractiveMode) { @@ -608,8 +616,18 @@ namespace MinecraftClient if (consoleReadThreadOwned) { - ConsoleIO.Backend.StopReadThread(); - consoleReadThreadOwned = false; + try + { + ConsoleIO.Backend.StopReadThread(); + } + catch (Exception ex) + { + ConsoleIO.WriteLineFormatted($"§c[MCC] Failed to stop console input thread during session teardown: {ex.Message}"); + } + finally + { + consoleReadThreadOwned = false; + } } } @@ -893,6 +911,9 @@ namespace MinecraftClient { instance = null; + // Ensure console session cleanup before disconnecting the network and bots. + StopConsoleSession(); + DispatchBotEvent(bot => bot.OnDisconnect(ChatBot.DisconnectReason.UserLogout, "")); foreach (ChatBot bot in bots.Where(bot => bot.ScriptOwnerKey is not null).ToList()) @@ -923,24 +944,97 @@ namespace MinecraftClient client.Close(); } + /// + /// Force stops the current client without raising bot disconnect events. + /// Used by restart cleanup when the normal disconnect path has timed out. + /// + public void ForceClose() + { + instance = null; + + try + { + StopConsoleSession(); + } + catch { } + + try + { + foreach (ChatBot bot in bots.Where(bot => bot.ScriptOwnerKey is not null).ToList()) + BotUnLoad(bot); + + botsOnHold.Clear(); + botsOnHold.AddRange(bots.Where(bot => bot.ScriptOwnerKey is null)); + } + catch { } + + try + { + if (handler is not null) + { + handler.Disconnect(); + handler.Dispose(); + } + } + catch { } + + try + { + if (timeoutdetector is not null) + { + timeoutdetector.Item2.Cancel(); + timeoutdetector = null; + } + } + catch { } + + try + { + client?.Close(); + } + catch { } + } + /// /// When connection has been lost, login was denied or played was kicked from the server /// public void OnConnectionLost(ChatBot.DisconnectReason reason, string message) { + if (Program.HasRestartPendingForAnotherThread) + { + Log.Info("[MCC] Connection lost event ignored because a restart is already in progress."); + return; + } + instance = null; - ConsoleIO.CancelAutocomplete(); + try + { + ConsoleIO.CancelAutocomplete(); + } + catch { } - handler.Dispose(); + try + { + handler?.Dispose(); + } + catch { } + finally + { + handler = null; + } world.Clear(); ClearKnownSigns(); if (timeoutdetector is not null) { - if (timeoutdetector is not null && Thread.CurrentThread != timeoutdetector.Item1) - timeoutdetector.Item2.Cancel(); + try + { + if (Thread.CurrentThread != timeoutdetector.Item1) + timeoutdetector.Item2.Cancel(); + } + catch { } timeoutdetector = null; }