Merge pull request #18 from milutinke/bugfix/autorelog-retry-accounting

bugfix: Fix AutoRelog retry accounting
This commit is contained in:
Anon 2026-06-02 10:29:52 +02:00 committed by GitHub
commit 03e2e0d3fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 113 additions and 21 deletions

View file

@ -1,4 +1,5 @@
using System;
using System.Threading;
using MinecraftClient.Scripting;
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>
/// This bot automatically re-join the server if kick message contains predefined string
@ -97,7 +100,13 @@ namespace MinecraftClient.ChatBots
public override void AfterGameJoined()
{
Configs._BotRecoAttempts = 0;
lock (s_reconnectStateLock)
s_lastJoinUtc = DateTime.UtcNow;
}
public override void Update()
{
ResetRetriesAfterStableJoin();
}
private void _Initialize()
@ -115,7 +124,11 @@ namespace MinecraftClient.ChatBots
{
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);
string comp = message.ToLower();
@ -124,18 +137,14 @@ namespace MinecraftClient.ChatBots
if (Config.Ignore_Kick_Message)
{
Configs._BotRecoAttempts++;
LaunchDelayedReconnection(null);
return true;
return LaunchDelayedReconnection(null);
}
foreach (string msg in Config.Kick_Messages)
{
if (comp.Contains(msg))
{
Configs._BotRecoAttempts++;
LaunchDelayedReconnection(msg);
return true;
return LaunchDelayedReconnection(msg);
}
}
@ -145,21 +154,83 @@ namespace MinecraftClient.ChatBots
return false;
}
private void LaunchDelayedReconnection(string? msg)
private static bool CanReconnect()
{
double delay = random.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));
lock (s_reconnectStateLock)
return Config.Retries < 0 || Configs._BotRecoAttempts < Config.Retries;
}
int retriesLeft = Config.Retries - Configs._BotRecoAttempts;
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;
}
}
string retriesDisplay = Config.Retries == int.MaxValue
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));
string retriesDisplay = HasUnlimitedRetries()
? Translations.bot_autoRelog_retries_unlimited
: retriesLeft.ToString();
McClient.ReconnectionAttemptsLeft = retriesLeft;
if (Program.TryRestart((int)Math.Floor(delay), true))
{
LogToConsole(string.Format(Translations.bot_autoRelog_wait_with_retries, delay, retriesDisplay));
ReconnectToTheServer(retriesLeft, (int)Math.Floor(delay), true);
return true;
}
RollBackReconnectAttempt();
return true;
}
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="keepAccountAndServerSettings">Optional, keep account and server settings</param>
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)
{
if (_restartThread is not null && _restartThread.IsAlive
&& _restartThread != Thread.CurrentThread)
return;
if (HasRestartPendingForAnotherThreadNoLock())
return false;
ConsoleIO.Backend?.StopReadThread();
var thread = new Thread(new ThreadStart(delegate
@ -938,9 +951,17 @@ namespace MinecraftClient
}));
_restartThread = thread;
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)
{
WriteBackSettings();