diff --git a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs index ffbd4233..e781aa18 100755 --- a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs +++ b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs @@ -143,32 +143,18 @@ namespace MinecraftClient.Protocol.Handlers.Forge case FMLVersion.FML3: // Example ModInfo for Minecraft 1.18 and greater (FML3) - // { - // "enforcesSecureChat": true, - // "forgeData": { - // "channels": [], - // "mods": [], - // "truncated": false, // legacy versions see truncated lists, modern versions ignore this truncated flag (binary data has its own) - // "fmlNetworkVersion": 3, - // "d": "ȳ\u0000\u0000ࠨ㐤獋㙖⹌ᦘ̺⸱恤䒸⡑⛧沮婙㨹牥ఈㄵচ₀沮婙㨹牥ఈㄵচ倠⹡岙㜲獥䋊㷍᭳ႇׇ஌᜘㘴娘▅筳ص䰭宛㘲、\u0000ᠸጋ囗湌夜㘲杩棐䐱ᅱ挃☥ోᤗ㌮ఀ׈䬣 坖ɍ䮌ᤘ\r\n旉䠳ዣ◆䲌㜃瑥廮ⷉࠋ–䁠奚Ҵ㔱摜䂸ᅱ獳ౠᡚ㜷汥戊䂸űဓĠ嵛㖱数嫤Ǎ塰䛶ⶎᮚ㞳晲擞ᖝ″ዣ䘆ఋʂ潦令ඕ爈䖔⺁ᥚ⾹潳棤㦥ᬻ挐؅䅀㠹楬ۨ㣄উ瀀渀嬛㘼扩搢䃀熁挂♥\r\n墋㒺摬牜ࣜ䁠嘗湌孛㜴浩惂䠙熙排٥孁㒰ͮ屢Ӏ䠐⚐䷮ᣛ㊴瑳戚䢸熁匒إ஍᜚ܴ䫜巑፻ᚷؠ䀀ㆃ牵䋨㦥ࠫ㋣䗆䂌㨈慲䫬ᖱᮓᘧ汬尚ㆰ٫屲㣄ᆉ恳ಭ川㤷፫擨妅挫♖乮塘 㖱慰\r\n囆䓩\t" - // }, - // "description": { - // "text": "A Minecraft Server" - // }, - // "players": { - // "max": 100, - // "online": 0 - // }, - // "version": { - // "name": "1.20.1", - // "protocol": 763 - // } - // } + // "forgeData": { + // "channels": [], + // "mods": [], + // "truncated": false, // legacy versions see truncated lists, modern versions ignore this truncated flag (binary data has its own) + // "fmlNetworkVersion": 3, + // "d": "ȳ\u0000\u0000ࠨ㐤獋㙖⹌ᦘ̺⸱恤䒸⡑⛧沮婙㨹牥ఈㄵচ₀沮婙㨹牥ఈㄵচ倠⹡岙㜲獥䋊㷍᭳ႇׇ஌᜘㘴娘▅筳ص䰭宛㘲、\u0000ᠸጋ囗湌夜㘲杩棐䐱ᅱ挃☥ోᤗ㌮ఀ׈䬣 坖ɍ䮌ᤘ\r\n旉䠳ዣ◆䲌㜃瑥廮ⷉࠋ–䁠奚Ҵ㔱摜䂸ᅱ獳ౠᡚ㜷汥戊䂸űဓĠ嵛㖱数嫤Ǎ塰䛶ⶎᮚ㞳晲擞ᖝ″ዣ䘆ఋʂ潦令ඕ爈䖔⺁ᥚ⾹潳棤㦥ᬻ挐؅䅀㠹楬ۨ㣄উ瀀渀嬛㘼扩搢䃀熁挂♥\r\n墋㒺摬牜ࣜ䁠嘗湌孛㜴浩惂䠙熙排٥孁㒰ͮ屢Ӏ䠐⚐䷮ᣛ㊴瑳戚䢸熁匒إ஍᜚ܴ䫜巑፻ᚷؠ䀀ㆃ牵䋨㦥ࠫ㋣䗆䂌㨈慲䫬ᖱᮓᘧ汬尚ㆰ٫屲㣄ᆉ恳ಭ川㤷፫擨妅挫♖乮塘 㖱慰\r\n囆䓩\t" + // } - // All buffer data are encoded and write to forgeData["d"] + // 1.18 and greater, the mod list and channel list is compressed to forgeData["d"] for efficiency, + // - Here is how forge encode and decode them: // https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L264 - - // 1.18 and greater, the buffer is encoded for efficiency + // - Here is the discussion: // see https://github.com/MinecraftForge/MinecraftForge/pull/8169 string encodedData = data.Properties["d"].StringValue; @@ -176,14 +162,21 @@ namespace MinecraftClient.Protocol.Handlers.Forge DataTypes dataTypes = new DataTypes(Protocol18Handler.MC_1_18_1_Version); // - // [truncated][boolean] placeholder for whether we are truncating - // [Mod Size][unsigned short] short so that we can replace it later in case of truncation + // [ Truncated ][ Bool ] // Unused + // [ Mod Size ][ Unsigned short ] // - bool truncated = dataTypes.ReadNextBool(dataPackage); + dataTypes.ReadNextBool(dataPackage); // truncated: boolean var modsSize = dataTypes.ReadNextUShort(dataPackage); - Dictionary channels = new(); Dictionary mods = new(); + // Mod Array Definition: + // [ Channel Size And Version Flag ][ VarInt ] // If the value at bit Mask 0x01 is 1, The Mod Version will be ignore. + // // The one-right-shifted int is the Channel List size. + // [ Mod Id ][ String ] + // [ Mod Version ][ Optional String ] // Depends on the Flag above + // [ Channel List ][ Array ] [ Channel Name ][ String ] + // [ Channel Version ][ String ] + // [ Required On Client ][ Bool ] for (var i = 0; i < modsSize; i++) { var channelSizeAndVersionFlag = dataTypes.ReadNextVarInt(dataPackage); @@ -194,27 +187,24 @@ namespace MinecraftClient.Protocol.Handlers.Forge var modId = dataTypes.ReadNextString(dataPackage); - string IGNORESERVERONLY = "";// it was "OHNOES\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31"; + string IGNORESERVERONLY = "IGNORED"; var modVersion = isIgnoreServerOnly ? IGNORESERVERONLY : dataTypes.ReadNextString(dataPackage); for (var i1 = 0; i1 < channelSize; i1++) { - var channelName = dataTypes.ReadNextString(dataPackage); - var channelVersion = dataTypes.ReadNextString(dataPackage); - var requiredOnClient = dataTypes.ReadNextBool(dataPackage); - channels.Add(modId + ":" + channelName, channelVersion + ":" + requiredOnClient); + dataTypes.ReadNextString(dataPackage); // channelName + dataTypes.ReadNextString(dataPackage); // channelVersion + dataTypes.ReadNextBool(dataPackage); // requiredOnClient } mods.Add(modId, modVersion); Mods.Add(new ForgeMod(modId, modVersion)); } - var nonModChannelCount = dataTypes.ReadNextVarInt(dataPackage); - for (var i = 0; i < nonModChannelCount; i++) { - var channelName = dataTypes.ReadNextString(dataPackage); - var channelVersion = dataTypes.ReadNextString(dataPackage); - var requiredOnClient = dataTypes.ReadNextBool(dataPackage); - channels.Add(channelName, channelVersion + ":" + requiredOnClient); - } + // Ignore the left data, which is NonMod Channel List + // [ nonMod Channel Count ][ VarInt ] + // [ nonMod Channel List ][ Array ] [ Channel Name ][ String ] + // [ Channel Version ][ Bool ] + // [ Required On Client ][ Bool ] break; default: @@ -222,10 +212,17 @@ namespace MinecraftClient.Protocol.Handlers.Forge } } - // https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L361 - // Decode binary data ForgeData["d"] to Queue + /// + /// Decompress binary data ForgeData["d"] (FML 3) + /// + /// The encoded data. + /// Decoded forge data Queue. + /// + /// 1.18 and greater, the mod list and channel list is compressed for efficiency + /// The code below is converted from forge source code, see: + /// https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L361 + /// private static Queue decodeOptimized(string encodedData) { - // Console.WriteLine("Got encoded data:" + encodedData + ", decoding..."); int size0 = encodedData[0]; int size1 = encodedData[1]; int size = size0 | (size1 << 15); diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index 99fb4c72..cbda452b 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -417,21 +417,13 @@ namespace MinecraftClient.Protocol.Handlers case 5: // FML 3 // Server Config: FMLHandshakeMessages.java > S2CModData > decode() + // [ Size ][ VarInt ] + // [ Mod Data List ][ Array ] [ Mod Id ][ String ] + // [ Display Name ][ String ] + // [ Version ][ String ] // // We're ignoring this packet in MCC - /* - // Uncomment this code block if needed - var size = dataTypes.ReadNextVarInt(packetData); - Dictionary modsData = new(); - for (int i = 0; i < size; i++) - { - var modId = dataTypes.ReadNextString(packetData); - var displayName = dataTypes.ReadNextString(packetData); - var version = dataTypes.ReadNextString(packetData); - modsData.Add(modId, displayName + ":" + version); - } - */ if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + "Received FML3 Server Mod Data List"); @@ -441,20 +433,11 @@ namespace MinecraftClient.Protocol.Handlers case 6: // FML 3 // Server Config: FMLHandshakeMessages.java > S2CChannelMismatchData > decode() - // + // [ Size ][ VarInt ] + // [ Mismatched Mod List ][ Array ] [ Mod Id ][ String ] + // [ Version ][ String ] // We're ignoring this packet in MCC - /* - // Uncomment this code block if needed - Dictionary mismatchedMods = new(); - var size0 = dataTypes.ReadNextVarInt(packetData); - for (int i = 0; i < size0; i++) - { - var modId = dataTypes.ReadNextString(packetData); - var version = dataTypes.ReadNextString(packetData); - mismatchedMods.Add(modId, version); - } - */ if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLineFormatted("§8" + "Received FML3 Server Mismatched Mods List"); @@ -523,15 +506,15 @@ namespace MinecraftClient.Protocol.Handlers /// /// Minecraft protocol version /// ForgeInfo item stating that Forge is enabled + /// + /// 1.18 change the fml version to 3 + /// https://github.com/MinecraftForge/MinecraftForge/commit/997d8e0aa28b831edcd712e59a96181d3b2117d4 + /// public static ForgeInfo ServerForceForge(int protocolVersion) { if (ServerMayForceForge(protocolVersion)) - { - // 1.17 is still FML2 - // https://github.com/MinecraftForge/MinecraftForge/blob/50b5414033de82f46be23201db50484f36c37d4f/src/main/java/net/minecraftforge/fmllegacy/network/FMLNetworkConstants.java#L37C29-L37C42 - // 1.18 change the constant FMLNETVERSION to 3 - // https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/NetworkConstants.java#L28 - if (protocolVersion > ProtocolHandler.MCVer2ProtocolVersion("1.18")) + { + if (protocolVersion >= ProtocolHandler.MCVer2ProtocolVersion("1.18")) { return new ForgeInfo(FMLVersion.FML3); } diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs index 7656c990..708e99d9 100644 --- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs +++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs @@ -5,7 +5,6 @@ using System.Text; using MinecraftClient.Protocol.Handlers; using MinecraftClient.Protocol.Message; using static MinecraftClient.Protocol.Message.LastSeenMessageList; -using Newtonsoft.Json.Linq; namespace MinecraftClient.Protocol.ProfileKey { @@ -20,7 +19,8 @@ namespace MinecraftClient.Protocol.ProfileKey ProxiedWebRequest.Response? response = null; try { - if (!isYggdrasil) { + if (!isYggdrasil) + { var request = new ProxiedWebRequest(certificates) { Accept = "application/json" @@ -37,8 +37,7 @@ namespace MinecraftClient.Protocol.ProfileKey // 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 - string jsonString = isYggdrasil ? MakeDummyResponse() : response!.Body; - Json.JSONData json = Json.ParseJson(jsonString); + Json.JSONData json = isYggdrasil ? MakeDummyResponse() : Json.ParseJson(response!.Body); // Error here PublicKey publicKey = new(pemKey: json.Properties["keyPair"].Properties["publicKey"].StringValue, sig: json.Properties["publicKeySignature"].StringValue, @@ -236,7 +235,7 @@ namespace MinecraftClient.Protocol.ProfileKey return sb.ToString(); } - public static string MakeDummyResponse() + public static Json.JSONData MakeDummyResponse() { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); var mimePublicKey = Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo()); @@ -246,19 +245,19 @@ namespace MinecraftClient.Protocol.ProfileKey DateTime now = DateTime.UtcNow; DateTime expiresAt = now.AddHours(48); DateTime refreshedAfter = now.AddHours(36); - JObject response = new JObject(); - JObject keyPairObj = new JObject - { - { "privateKey", privateKeyPEM }, - { "publicKey", publicKeyPEM } - }; - response.Add("keyPair", keyPairObj); - response.Add("publicKeySignature", "AA=="); - response.Add("publicKeySignatureV2", "AA=="); + Json.JSONData response = new(Json.JSONData.DataType.Object); + Json.JSONData keyPairObj = new(Json.JSONData.DataType.Object); + keyPairObj.Properties["privateKey"] = new(Json.JSONData.DataType.String){ StringValue = privateKeyPEM }; + keyPairObj.Properties["publicKey"] = new(Json.JSONData.DataType.String){ StringValue = publicKeyPEM }; + + response.Properties["keyPair"] = keyPairObj; + response.Properties["publicKeySignature"] = new(Json.JSONData.DataType.String){ StringValue = "AA==" }; + response.Properties["publicKeySignatureV2"] = new(Json.JSONData.DataType.String){ StringValue = "AA==" }; string format = "yyyy-MM-ddTHH:mm:ss.ffffffZ"; - response.Add("expiresAt", expiresAt.ToString(format)); - response.Add("refreshedAfter", refreshedAfter.ToString(format)); - return response.ToString(); + response.Properties["expiresAt"] = new(Json.JSONData.DataType.String){ StringValue = expiresAt.ToString(format) }; + response.Properties["refreshedAfter"] = new(Json.JSONData.DataType.String){ StringValue = refreshedAfter.ToString(format) }; + + return response; } } }