diff --git a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs index db46829b..ffbd4233 100755 --- a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs +++ b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs @@ -141,16 +141,14 @@ namespace MinecraftClient.Protocol.Handlers.Forge break; case FMLVersion.FML3: - // All buf data are write do forgeData["d"] - // src/main/java/net/minecraftforge/network/ServerStatusPing.java > serialize(ServerStatusPing forgeData) - // src/main/java/net/minecraftforge/network/ServerStatusPing.java > deserializeOptimized(JsonObject forgeData) + // Example ModInfo for Minecraft 1.18 and greater (FML3) // { // "enforcesSecureChat": true, // "forgeData": { // "channels": [], // "mods": [], - // "truncated": false, + // "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" // }, @@ -166,19 +164,22 @@ namespace MinecraftClient.Protocol.Handlers.Forge // "protocol": 763 // } // } + + // All buffer data are encoded and write to forgeData["d"] + // 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 + // see https://github.com/MinecraftForge/MinecraftForge/pull/8169 + string encodedData = data.Properties["d"].StringValue; Queue dataPackage = decodeOptimized(encodedData); - DataTypes dataTypes = new DataTypes(Protocol18Handler.MC_1_18_2_Version); + 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 // - // Console.WriteLine("decodedData="); - bool truncated = false; - //Map> channels; - - truncated = dataTypes.ReadNextBool(dataPackage); + bool truncated = dataTypes.ReadNextBool(dataPackage); var modsSize = dataTypes.ReadNextUShort(dataPackage); Dictionary channels = new(); @@ -193,7 +194,7 @@ namespace MinecraftClient.Protocol.Handlers.Forge var modId = dataTypes.ReadNextString(dataPackage); - string IGNORESERVERONLY = "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 = "";// 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"; var modVersion = isIgnoreServerOnly ? IGNORESERVERONLY : dataTypes.ReadNextString(dataPackage); for (var i1 = 0; i1 < channelSize; i1++) { @@ -201,7 +202,6 @@ namespace MinecraftClient.Protocol.Handlers.Forge var channelVersion = dataTypes.ReadNextString(dataPackage); var requiredOnClient = dataTypes.ReadNextBool(dataPackage); channels.Add(modId + ":" + channelName, channelVersion + ":" + requiredOnClient); - //channels.Add(new ResourceLocation(modId, channelName), Pair.of(channelVersion, requiredOnClient)); } mods.Add(modId, modVersion); @@ -214,15 +214,6 @@ namespace MinecraftClient.Protocol.Handlers.Forge var channelVersion = dataTypes.ReadNextString(dataPackage); var requiredOnClient = dataTypes.ReadNextBool(dataPackage); channels.Add(channelName, channelVersion + ":" + requiredOnClient); - // channels.put(channelName, Pair.of(channelVersion, requiredOnClient)); - } - - foreach (var key in channels.Keys) { - //Console.WriteLine("We got channel: " + key + "-" + channels[key]); - } - - foreach (var key in mods.Keys) { - //Console.WriteLine("We got mod: " + key + "-" + mods[key]); } break; @@ -231,12 +222,12 @@ namespace MinecraftClient.Protocol.Handlers.Forge } } - // decode ForgeData["d"] to Queue - // see src/main/java/net/minecraftforge/network/ServerStatusPing.java#l361 - public static Queue decodeOptimized(string encodedData) { + // https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L361 + // Decode binary data ForgeData["d"] to Queue + private static Queue decodeOptimized(string encodedData) { // Console.WriteLine("Got encoded data:" + encodedData + ", decoding..."); - int size0 = (int)encodedData[0]; - int size1 = (int)encodedData[1]; + int size0 = encodedData[0]; + int size1 = encodedData[1]; int size = size0 | (size1 << 15); List packageData = new(); @@ -255,7 +246,7 @@ namespace MinecraftClient.Protocol.Handlers.Forge } char c = encodedData[stringIndex]; - buffer |= ((int)c & 0x7FFF) << bitsInBuf; + buffer |= (c & 0x7FFF) << bitsInBuf; bitsInBuf += 15; stringIndex++; } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index d6d58e87..99fb4c72 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -233,7 +233,7 @@ namespace MinecraftClient.Protocol.Handlers } /// - /// Handle Forge plugin messages during login phase (Forge Protocol version 2: FML2) + /// Handle Forge plugin messages during login phase (Forge Protocol version 2: FML2 or Forge Protocol version 3: FML3) /// /// Plugin message channel /// Plugin message data @@ -244,12 +244,16 @@ namespace MinecraftClient.Protocol.Handlers if (ForgeEnabled() && (forgeInfo!.Version == FMLVersion.FML2 || forgeInfo!.Version == FMLVersion.FML3) && channel == "fml:loginwrapper") { // Forge Handshake handler source code used to implement the FML2 packets: - // https://github.com/MinecraftForge/MinecraftForge/blob/master/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java - // https://github.com/MinecraftForge/MinecraftForge/blob/master/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java - // https://github.com/MinecraftForge/MinecraftForge/blob/master/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java - // https://github.com/MinecraftForge/MinecraftForge/blob/master/src/main/java/net/minecraftforge/fml/network/FMLLoginWrapper.java - // https://github.com/MinecraftForge/MinecraftForge/blob/master/src/main/java/net/minecraftforge/fml/network/FMLHandshakeMessages.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.13.x/src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.13.x/src/main/java/net/minecraftforge/fml/network/FMLHandshakeHandler.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.13.x/src/main/java/net/minecraftforge/fml/network/NetworkInitialization.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.13.x/src/main/java/net/minecraftforge/fml/network/FMLLoginWrapper.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.13.x/src/main/java/net/minecraftforge/fml/network/FMLHandshakeMessages.java // + // FML3 packets: + // https://github.com/MinecraftForge/MinecraftForge/blob/1.18.x/src/main/java/net/minecraftforge/network/NetworkInitialization.java + // https://github.com/MinecraftForge/MinecraftForge/blob/1.18.x/src/main/java/net/minecraftforge/fml/network/FMLHandshakeMessages.java + // // During Login, Forge will send a set of LoginPluginRequest packets and we need to respond accordingly. // Each login plugin message contains in its payload field an inner packet created by FMLLoginWrapper.java: // @@ -274,6 +278,8 @@ namespace MinecraftClient.Protocol.Handlers // 2 = Client to Server - Mod List // 3 = Server to Client - Registry // 4 = Server to Client - Config + // 5 = Server to Client - Mod Data List (FML3) + // 6 = Server to Client - MismatchedMod List (FML3) // // The content of each message is mapped into a class inside FMLHandshakeMessages.java // FMLHandshakeHandler will then process the packet, e.g. handleServerModListOnClient() for Server Mod List. @@ -320,6 +326,7 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < registryCount; i++) registries.Add(dataTypes.ReadNextString(packetData)); + // FML3 specific, List dataPackRegistries = new(); if (forgeInfo!.Version == FMLVersion.FML3 && packetData.Count != 0) { @@ -407,6 +414,53 @@ namespace MinecraftClient.Protocol.Handlers fmlResponseReady = true; break; + case 5: + // FML 3 + // Server Config: FMLHandshakeMessages.java > S2CModData > decode() + // + // 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"); + } + break; + + case 6: + // FML 3 + // Server Config: FMLHandshakeMessages.java > S2CChannelMismatchData > decode() + // + // 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"); + } + break; + default: if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_unknown, packetID)); @@ -450,7 +504,7 @@ namespace MinecraftClient.Protocol.Handlers public static bool ServerInfoCheckForge(Json.JSONData jsonData, ref ForgeInfo? forgeInfo) { return ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML) // MC 1.12 and lower - || ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML2) // MC 1.13 and greater + || ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML2) // MC 1.13 to 1.17 || ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML3); // MC 1.18 and greater } @@ -472,14 +526,22 @@ namespace MinecraftClient.Protocol.Handlers public static ForgeInfo ServerForceForge(int protocolVersion) { if (ServerMayForceForge(protocolVersion)) - { - return new ForgeInfo(FMLVersion.FML3); + { + // 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")) + { + return new ForgeInfo(FMLVersion.FML3); + } + return new ForgeInfo(FMLVersion.FML2); } else throw new InvalidOperationException(Translations.error_forgeforce); } /// - /// Server Info: Check for For Forge on a Minecraft server Ping result (Handles FML and FML2 + /// Server Info: Check for For Forge on a Minecraft server Ping result (Handles FML and FML2 and FML3 /// /// JSON data returned by the server /// ForgeInfo to populate