Fixed transfering breaking on multiple versions

This commit is contained in:
Anon 2026-03-25 14:05:43 +01:00
parent 52eda4ccdd
commit 41a1a1db9b
4 changed files with 81 additions and 17 deletions

View file

@ -75,8 +75,8 @@ namespace MinecraftClient
private int sequenceId; // User for player block synchronization (Aka. digging, placing blocks, etc..) private int sequenceId; // User for player block synchronization (Aka. digging, placing blocks, etc..)
private bool CanSendMessage = false; private bool CanSendMessage = false;
private readonly string host; private string host;
private readonly int port; private int port;
private readonly int protocolversion; private readonly int protocolversion;
private readonly string username; private readonly string username;
private Guid uuid; private Guid uuid;
@ -159,6 +159,7 @@ namespace MinecraftClient
SessionToken _sessionToken; SessionToken _sessionToken;
CancellationTokenSource? cmdprompt = null; CancellationTokenSource? cmdprompt = null;
Tuple<Thread, CancellationTokenSource>? timeoutdetector = null; Tuple<Thread, CancellationTokenSource>? timeoutdetector = null;
private int transferInProgress = 0;
public ILogger Log; public ILogger Log;
@ -335,16 +336,34 @@ namespace MinecraftClient
public void Transfer(string newHost, int newPort) public void Transfer(string newHost, int newPort)
{ {
// Do not block here: a new handler can start processing packets before the
// previous transfer call fully unwinds, and waiting can deadlock main-thread work.
if (Interlocked.CompareExchange(ref transferInProgress, 1, 0) != 0)
{
Log.Warn($"Ignoring overlapping transfer to {newHost}:{newPort} because another transfer is still in progress.");
return;
}
IMinecraftCom oldHandler = handler;
TcpClient oldClient = client;
try try
{ {
Log.Info($"Initiating a transfer to: {host}:{port}"); Log.Info($"Initiating a transfer to: {newHost}:{newPort}");
// Unload bots // Unload bots
UnloadAllBots(); UnloadAllBots();
bots.Clear(); bots.Clear();
ResetStateForTransfer();
// Close existing connection // Retire the old handler so its updater exits without reporting a stale disconnect.
client.Close(); oldHandler.Dispose();
oldClient.Close();
host = newHost;
port = newPort;
UpdateKeepAlive();
// Establish new connection // Establish new connection
client = ProxyHandler.NewTcpClient(newHost, newPort); client = ProxyHandler.NewTcpClient(newHost, newPort);
@ -353,20 +372,17 @@ namespace MinecraftClient
// Reinitialize the protocol handler // Reinitialize the protocol handler
handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, null, this); handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, null, this);
Log.Info($"Connected to {host}:{port}"); Log.Info($"Connected to {newHost}:{newPort}");
// Retry login process // Retry login process
if (handler.Login(playerKeyPair, _sessionToken)) if (handler.Login(playerKeyPair, _sessionToken, isTransfer: true))
{ {
foreach (var bot in botsOnHold) foreach (var bot in botsOnHold)
BotLoad(bot, false); BotLoad(bot, false);
botsOnHold.Clear(); botsOnHold.Clear();
Log.Info("Successfully transferred connection and logged in."); UpdateKeepAlive();
cmdprompt = new CancellationTokenSource(); Log.Info($"Successfully transferred connection and logged in to {newHost}:{newPort}.");
ConsoleInteractive.ConsoleReader.BeginReadThread();
ConsoleInteractive.ConsoleReader.MessageReceived += ConsoleReaderOnMessageReceived;
ConsoleInteractive.ConsoleReader.OnInputChange += ConsoleIO.AutocompleteHandler;
} }
else else
{ {
@ -378,6 +394,22 @@ namespace MinecraftClient
{ {
Log.Error($"Transfer to {newHost}:{newPort} failed: {ex.Message}"); Log.Error($"Transfer to {newHost}:{newPort} failed: {ex.Message}");
try
{
handler.Dispose();
}
catch
{
}
try
{
client.Close();
}
catch
{
}
// Handle reconnection attempts // Handle reconnection attempts
if (timeoutdetector is not null) if (timeoutdetector is not null)
{ {
@ -400,8 +432,35 @@ namespace MinecraftClient
Program.HandleFailure(); Program.HandleFailure();
} }
throw new Exception("Transfer failed and reconnection attempts exhausted."); throw new Exception("Transfer failed and reconnection attempts exhausted.", ex);
} }
finally
{
Interlocked.Exchange(ref transferInProgress, 0);
}
}
private void ResetStateForTransfer()
{
ClearTasks();
ConsoleIO.CancelAutocomplete();
SetCanSendMessage(false);
locationReceived = false;
physicsInitialized = false;
isUnderSlab = false;
path = null;
pathTarget = null;
_yaw = null;
_pitch = null;
LastDigPosition = null;
RemainingDiggingTime = 0;
nextSneakingUpdate = DateTime.Now;
physicsInput.Reset();
world.Clear();
entities.Clear();
ClearInventories();
} }
/// <summary> /// <summary>

View file

@ -618,7 +618,7 @@ namespace MinecraftClient.Protocol.Handlers
} }
} }
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
{ {
if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session)) if (Handshake(handler.GetUserUuidStr(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session))
{ {

View file

@ -3188,8 +3188,13 @@ namespace MinecraftClient.Protocol.Handlers
/// Do the Minecraft login. /// Do the Minecraft login.
/// </summary> /// </summary>
/// <returns>True if login successful</returns> /// <returns>True if login successful</returns>
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session, bool isTransfer = false)
{ {
int nextState = isTransfer && protocolVersion >= MC_1_20_6_Version ? 3 : 2;
if (nextState == 3)
log.Debug("Using transfer handshake intent for transferred login.");
// 1. Send the handshake packet // 1. Send the handshake packet
SendPacket(0x00, dataTypes.ConcatBytes( SendPacket(0x00, dataTypes.ConcatBytes(
// Protocol Version (use raw version for snapshot/RC servers) // Protocol Version (use raw version for snapshot/RC servers)
@ -3202,7 +3207,7 @@ namespace MinecraftClient.Protocol.Handlers
dataTypes.GetUShort((ushort)handler.GetServerPort()), dataTypes.GetUShort((ushort)handler.GetServerPort()),
// Next State // Next State
DataTypes.GetVarInt(2)) // 2 is for the Login state DataTypes.GetVarInt(nextState)) // 2 is Login, 3 is Transfer
); );
// 2. Send the Login Start packet // 2. Send the Login Start packet

View file

@ -19,7 +19,7 @@ namespace MinecraftClient.Protocol
/// Start the login procedure once connected to the server /// Start the login procedure once connected to the server
/// </summary> /// </summary>
/// <returns>True if login was successful</returns> /// <returns>True if login was successful</returns>
bool Login(PlayerKeyPair? playerKeyPair, Session.SessionToken session); bool Login(PlayerKeyPair? playerKeyPair, Session.SessionToken session, bool isTransfer = false);
/// <summary> /// <summary>
/// Disconnect from the server /// Disconnect from the server