Fix AutoRelog retry accounting

This commit is contained in:
Anon 2026-06-02 10:24:59 +02:00
parent f39fe2d067
commit 47de24cf1d
2 changed files with 113 additions and 21 deletions

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using MinecraftClient.Scripting; using MinecraftClient.Scripting;
using Tomlet.Attributes; using Tomlet.Attributes;
@ -77,7 +78,9 @@ namespace MinecraftClient.ChatBots
} }
} }
private static readonly Random random = new(); private static readonly Lock s_reconnectStateLock = new();
private static readonly TimeSpan s_stableJoinBeforeRetryReset = TimeSpan.FromSeconds(60);
private static DateTime? s_lastJoinUtc;
/// <summary> /// <summary>
/// This bot automatically re-join the server if kick message contains predefined string /// This bot automatically re-join the server if kick message contains predefined string
@ -97,7 +100,13 @@ namespace MinecraftClient.ChatBots
public override void AfterGameJoined() public override void AfterGameJoined()
{ {
Configs._BotRecoAttempts = 0; lock (s_reconnectStateLock)
s_lastJoinUtc = DateTime.UtcNow;
}
public override void Update()
{
ResetRetriesAfterStableJoin();
} }
private void _Initialize() private void _Initialize()
@ -115,7 +124,11 @@ namespace MinecraftClient.ChatBots
{ {
LogDebugToConsole(Translations.bot_autoRelog_ignore_user_logout); LogDebugToConsole(Translations.bot_autoRelog_ignore_user_logout);
} }
else if (Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries) else if (Program.HasRestartPendingForAnotherThread)
{
return true;
}
else if (CanReconnect())
{ {
message = GetVerbatim(message); message = GetVerbatim(message);
string comp = message.ToLower(); string comp = message.ToLower();
@ -124,18 +137,14 @@ namespace MinecraftClient.ChatBots
if (Config.Ignore_Kick_Message) if (Config.Ignore_Kick_Message)
{ {
Configs._BotRecoAttempts++; return LaunchDelayedReconnection(null);
LaunchDelayedReconnection(null);
return true;
} }
foreach (string msg in Config.Kick_Messages) foreach (string msg in Config.Kick_Messages)
{ {
if (comp.Contains(msg)) if (comp.Contains(msg))
{ {
Configs._BotRecoAttempts++; return LaunchDelayedReconnection(msg);
LaunchDelayedReconnection(msg);
return true;
} }
} }
@ -145,21 +154,83 @@ namespace MinecraftClient.ChatBots
return false; return false;
} }
private void LaunchDelayedReconnection(string? msg) private static bool CanReconnect()
{ {
double delay = random.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min; lock (s_reconnectStateLock)
return Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries;
}
private static void ResetRetriesAfterStableJoin()
{
lock (s_reconnectStateLock)
{
if (Configs._BotRecoAttempts <= 0 || s_lastJoinUtc is not DateTime lastJoinUtc)
return;
if (DateTime.UtcNow - lastJoinUtc < s_stableJoinBeforeRetryReset)
return;
Configs._BotRecoAttempts = 0;
s_lastJoinUtc = null;
McClient.ReconnectionAttemptsLeft = Config.Retries;
}
}
private static bool TryConsumeReconnectAttempt(out int retriesLeft)
{
lock (s_reconnectStateLock)
{
bool unlimitedRetries = HasUnlimitedRetries();
if (!unlimitedRetries && Configs._BotRecoAttempts >= Config.Retries)
{
retriesLeft = 0;
return false;
}
Configs._BotRecoAttempts++;
s_lastJoinUtc = null;
retriesLeft = unlimitedRetries ? int.MaxValue : Config.Retries - Configs._BotRecoAttempts;
if (retriesLeft < 0)
retriesLeft = 0;
return true;
}
}
private static bool HasUnlimitedRetries()
{
return Config.Retries < 0 || Config.Retries == int.MaxValue;
}
private static void RollBackReconnectAttempt()
{
lock (s_reconnectStateLock)
{
if (Configs._BotRecoAttempts > 0)
Configs._BotRecoAttempts--;
}
}
private bool LaunchDelayedReconnection(string? msg)
{
if (!TryConsumeReconnectAttempt(out int retriesLeft))
return false;
double delay = Random.Shared.NextDouble() * (Config.Delay.max - Config.Delay.min) + Config.Delay.min;
LogDebugToConsole(string.Format(string.IsNullOrEmpty(msg) ? Translations.bot_autoRelog_reconnect_always : Translations.bot_autoRelog_reconnect, msg)); LogDebugToConsole(string.Format(string.IsNullOrEmpty(msg) ? Translations.bot_autoRelog_reconnect_always : Translations.bot_autoRelog_reconnect, msg));
int retriesLeft = Config.Retries - Configs._BotRecoAttempts; string retriesDisplay = HasUnlimitedRetries()
if (retriesLeft < 0)
retriesLeft = 0;
string retriesDisplay = Config.Retries == int.MaxValue
? Translations.bot_autoRelog_retries_unlimited ? Translations.bot_autoRelog_retries_unlimited
: retriesLeft.ToString(); : retriesLeft.ToString();
LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay)); McClient.ReconnectionAttemptsLeft = retriesLeft;
ReconnectToTheServer(retriesLeft, (int)Math.Floor(delay), true); if (Program.TryRestart((int)Math.Floor(delay), true))
{
LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay));
return true;
}
RollBackReconnectAttempt();
return true;
} }
public static bool OnDisconnectStatic(DisconnectReason reason, string message) public static bool OnDisconnectStatic(DisconnectReason reason, string message)

View file

@ -899,12 +899,25 @@ namespace MinecraftClient
/// <param name="delaySeconds">Optional delay, in seconds, before restarting</param> /// <param name="delaySeconds">Optional delay, in seconds, before restarting</param>
/// <param name="keepAccountAndServerSettings">Optional, keep account and server settings</param> /// <param name="keepAccountAndServerSettings">Optional, keep account and server settings</param>
public static void Restart(int delaySeconds = 0, bool keepAccountAndServerSettings = false) public static void Restart(int delaySeconds = 0, bool keepAccountAndServerSettings = false)
{
TryRestart(delaySeconds, keepAccountAndServerSettings);
}
internal static bool HasRestartPendingForAnotherThread
{
get
{
lock (_restartLock)
return HasRestartPendingForAnotherThreadNoLock();
}
}
internal static bool TryRestart(int delaySeconds = 0, bool keepAccountAndServerSettings = false)
{ {
lock (_restartLock) lock (_restartLock)
{ {
if (_restartThread is not null && _restartThread.IsAlive if (HasRestartPendingForAnotherThreadNoLock())
&& _restartThread != Thread.CurrentThread) return false;
return;
ConsoleIO.Backend?.StopReadThread(); ConsoleIO.Backend?.StopReadThread();
var thread = new Thread(new ThreadStart(delegate var thread = new Thread(new ThreadStart(delegate
@ -938,9 +951,17 @@ namespace MinecraftClient
})); }));
_restartThread = thread; _restartThread = thread;
thread.Start(); thread.Start();
return true;
} }
} }
private static bool HasRestartPendingForAnotherThreadNoLock()
{
return _restartThread is not null
&& _restartThread.IsAlive
&& _restartThread != Thread.CurrentThread;
}
public static void DoExit(int exitcode = 0) public static void DoExit(int exitcode = 0)
{ {
WriteBackSettings(); WriteBackSettings();