Fix offline Microsoft auth profile-key checks and complete status ping/pong during auto-version detection.

This commit is contained in:
Anon 2026-03-22 22:19:59 +01:00
parent af405e5632
commit d44fc22c8a
3 changed files with 126 additions and 73 deletions

View file

@ -596,7 +596,8 @@ namespace MinecraftClient
if ((Config.Main.General.AccountType == LoginType.microsoft || Config.Main.General.AccountType == LoginType.yggdrasil)
&& (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser)
&& Config.Signature.LoginWithSecureProfile
&& protocolversion >= 759 /* 1.19 and above */)
&& protocolversion >= 759 /* 1.19 and above */
&& !string.IsNullOrWhiteSpace(session.ID))
{
// Load cached profile key from disk if necessary
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)

View file

@ -3520,81 +3520,113 @@ namespace MinecraftClient.Protocol.Handlers
/// <returns>True if ping was successful</returns>
public static bool DoPing(string host, int port, ref int protocolVersion, ref ForgeInfo? forgeInfo)
{
var version = "";
var tcp = ProxyHandler.NewTcpClient(host, port);
tcp.ReceiveTimeout = 30000; // 30 seconds
tcp.ReceiveBufferSize = 1024 * 1024;
SocketWrapper socketWrapper = new(tcp);
DataTypes dataTypes = new(MC_1_8_Version);
SocketWrapper? socketWrapper = null;
var serverPort = BitConverter.GetBytes((ushort)port);
Array.Reverse(serverPort);
// Ping Packet
var pingPacket = dataTypes.ConcatBytes(
// Packet Id
DataTypes.GetVarInt(0),
// Protocol Version
DataTypes.GetVarInt(-1),
// Server IP (Host)
dataTypes.GetString(host),
// Server port
serverPort,
// Next State
DataTypes.GetVarInt(1));
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket));
// Status Request Packet
var statusRequest = DataTypes.GetVarInt(0);
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
// Read Response length
var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
if (packetLength <= 0) return false;
// Read the Packet Id
var packetData = new Queue<byte>(socketWrapper.ReadDataRAW(packetLength));
if (dataTypes.ReadNextVarInt(packetData) != 0x00) return false;
var result = dataTypes.ReadNextString(packetData); // Get the Json data
if (Config.Logging.DebugMessages)
try
{
// May contain formatting codes, cannot use WriteLineFormatted
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
var version = "";
var tcp = ProxyHandler.NewTcpClient(host, port);
tcp.ReceiveTimeout = 30000; // 30 seconds
tcp.ReceiveBufferSize = 1024 * 1024;
socketWrapper = new SocketWrapper(tcp);
DataTypes dataTypes = new(MC_1_8_Version);
var serverPort = BitConverter.GetBytes((ushort)port);
Array.Reverse(serverPort);
// Ping Packet
var pingPacket = dataTypes.ConcatBytes(
// Packet Id
DataTypes.GetVarInt(0),
// Protocol Version
DataTypes.GetVarInt(-1),
// Server IP (Host)
dataTypes.GetString(host),
// Server port
serverPort,
// Next State
DataTypes.GetVarInt(1));
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket));
// Status Request Packet
var statusRequest = DataTypes.GetVarInt(0);
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
// Read Response length
var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
if (packetLength <= 0)
return false;
// Read the Packet Id
var packetData = new Queue<byte>(socketWrapper.ReadDataRAW(packetLength));
if (dataTypes.ReadNextVarInt(packetData) != 0x00)
return false;
var result = dataTypes.ReadNextString(packetData); // Get the Json data
if (Config.Logging.DebugMessages)
{
// May contain formatting codes, cannot use WriteLineFormatted
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
}
if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}"))
return false;
var jsonData = Json.ParseJson(result);
if (jsonData is not System.Text.Json.Nodes.JsonObject jsonObj || !jsonObj.ContainsKey("version"))
return false;
var versionData = jsonObj["version"]!.AsObject();
// Retrieve display name of the Minecraft version
if (versionData["name"] is { } nameNode)
version = nameNode.GetStringValue();
// Retrieve protocol version number for handling this server
if (versionData["protocol"] is { } protocolNode)
protocolVersion = int.Parse(protocolNode.GetStringValue(),
NumberStyles.Any, CultureInfo.CurrentCulture);
// Check for forge on the server.
Protocol18Forge.ServerInfoCheckForge(jsonObj, ref forgeInfo);
// Complete the normal status exchange so the probe connection closes cleanly server-side.
try
{
long pingPayload = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload));
socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest));
packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
if (packetLength > 0)
{
packetData = new Queue<byte>(socketWrapper.ReadDataRAW(packetLength));
if (dataTypes.ReadNextVarInt(packetData) == 0x01)
dataTypes.ReadNextLong(packetData);
}
}
catch
{
// Some servers may close the probe connection immediately after the status response.
}
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version,
protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
return true;
}
finally
{
socketWrapper?.Disconnect();
}
if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}")) return false;
var jsonData = Json.ParseJson(result);
if (jsonData is not System.Text.Json.Nodes.JsonObject jsonObj || !jsonObj.ContainsKey("version"))
return false;
var versionData = jsonObj["version"]!.AsObject();
//Retrieve display name of the Minecraft version
if (versionData["name"] is { } nameNode)
version = nameNode.GetStringValue();
//Retrieve protocol version number for handling this server
if (versionData["protocol"] is { } protocolNode)
protocolVersion = int.Parse(protocolNode.GetStringValue(),
NumberStyles.Any, CultureInfo.CurrentCulture);
// Check for forge on the server.
Protocol18Forge.ServerInfoCheckForge(jsonObj, ref forgeInfo);
ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version,
protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
return true;
}
/// <summary>

View file

@ -20,6 +20,9 @@ namespace MinecraftClient.Protocol.ProfileKey
ProxiedWebRequest.Response? response = null;
try
{
if (!isYggdrasil && string.IsNullOrWhiteSpace(accessToken))
return null;
if (!isYggdrasil)
{
var request = new ProxiedWebRequest(certificates)
@ -34,11 +37,28 @@ namespace MinecraftClient.Protocol.ProfileKey
{
ConsoleIO.WriteLine(response.Body.ToString());
}
if (response.StatusCode < 200 || response.StatusCode >= 300)
{
throw new InvalidOperationException(string.IsNullOrWhiteSpace(response.Body)
? "Certificate endpoint returned an error response."
: response.Body);
}
}
// see https://github.com/yushijinhun/authlib-injector/blob/da910956eaa30d2f6c2c457222d188aeb53b0d1f/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java#L49
// POST to "https://api.minecraftservices.com/player/certificates" with authlib-injector will get a dummy response
var json = isYggdrasil ? MakeDummyResponse() : Json.ParseJson(response!.Body);
if (json?["keyPair"]?["publicKey"] == null
|| json["keyPair"]?["privateKey"] == null
|| json["publicKeySignature"] == null
|| json["publicKeySignatureV2"] == null
|| json["expiresAt"] == null
|| json["refreshedAfter"] == null)
{
throw new InvalidOperationException("Certificate endpoint returned an unexpected payload.");
}
// Error here
PublicKey publicKey = new(pemKey: json!["keyPair"]!["publicKey"]!.GetStringValue(),
sig: json["publicKeySignature"]!.GetStringValue(),