diff --git a/MinecraftClient/Protocol/Handlers/Forge/FMLVersion.cs b/MinecraftClient/Protocol/Handlers/Forge/FMLVersion.cs index 2590d473..d9980dc3 100644 --- a/MinecraftClient/Protocol/Handlers/Forge/FMLVersion.cs +++ b/MinecraftClient/Protocol/Handlers/Forge/FMLVersion.cs @@ -7,6 +7,7 @@ enum FMLVersion { FML, - FML2 + FML2, + FML3 } } diff --git a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs index 781cfd5a..db46829b 100755 --- a/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs +++ b/MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs @@ -46,6 +46,13 @@ namespace MinecraftClient.Protocol.Handlers.Forge }; Version = fmlVersion; break; + case FMLVersion.FML3: + Mods = new List + { + new ForgeMod("forge", "ANY") + }; + Version = fmlVersion; + break; default: throw new InvalidOperationException(Translations.error_forgeforce); } @@ -133,10 +140,134 @@ 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) + + // { + // "enforcesSecureChat": true, + // "forgeData": { + // "channels": [], + // "mods": [], + // "truncated": false, + // "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 + // } + // } + string encodedData = data.Properties["d"].StringValue; + Queue dataPackage = decodeOptimized(encodedData); + DataTypes dataTypes = new DataTypes(Protocol18Handler.MC_1_18_2_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); + var modsSize = dataTypes.ReadNextUShort(dataPackage); + + Dictionary channels = new(); + Dictionary mods = new(); + + for (var i = 0; i < modsSize; i++) { + var channelSizeAndVersionFlag = dataTypes.ReadNextVarInt(dataPackage); + var channelSize = channelSizeAndVersionFlag >> 1; + + int VERSION_FLAG_IGNORESERVERONLY = 0b1; + var isIgnoreServerOnly = (channelSizeAndVersionFlag & VERSION_FLAG_IGNORESERVERONLY) != 0; + + 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"; + 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); + //channels.Add(new ResourceLocation(modId, channelName), Pair.of(channelVersion, 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); + // 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; default: throw new NotImplementedException("FMLVersion '" + fmlVersion + "' not implemented!"); } } + + // decode ForgeData["d"] to Queue + // see src/main/java/net/minecraftforge/network/ServerStatusPing.java#l361 + public static Queue decodeOptimized(string encodedData) { + // Console.WriteLine("Got encoded data:" + encodedData + ", decoding..."); + int size0 = (int)encodedData[0]; + int size1 = (int)encodedData[1]; + int size = size0 | (size1 << 15); + + List packageData = new(); + + int stringIndex = 2; + int buffer = 0; + int bitsInBuf = 0; + + while (stringIndex < encodedData.Length) + { + while (bitsInBuf >= 8) + { + packageData.Add((byte)buffer); + buffer >>= 8; + bitsInBuf -= 8; + } + + char c = encodedData[stringIndex]; + buffer |= ((int)c & 0x7FFF) << bitsInBuf; + bitsInBuf += 15; + stringIndex++; + } + + while (packageData.Count < size) + { + packageData.Add((byte)buffer); + buffer >>= 8; + bitsInBuf -= 8; + } + + return new Queue(packageData.ToArray()); + } } } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs index 1f1c5f4d..d6d58e87 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Forge.cs @@ -241,7 +241,7 @@ namespace MinecraftClient.Protocol.Handlers /// TRUE/FALSE depending on whether the packet was understood or not public bool HandleLoginPluginRequest(string channel, Queue packetData, ref List responseData) { - if (ForgeEnabled() && forgeInfo!.Version == FMLVersion.FML2 && channel == "fml:loginwrapper") + 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 @@ -320,6 +320,14 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < registryCount; i++) registries.Add(dataTypes.ReadNextString(packetData)); + List dataPackRegistries = new(); + if (forgeInfo!.Version == FMLVersion.FML3 && packetData.Count != 0) + { + int dataPackRegistryCount = dataTypes.ReadNextVarInt(packetData); + for (int i = 0; i < dataPackRegistryCount; i++) + dataPackRegistries.Add(dataTypes.ReadNextString(packetData)); + } + // Server Mod List Reply: FMLHandshakeMessages.java > C2SModListReply > encode() // // [ Mod Count ][ VarInt ] @@ -375,7 +383,7 @@ namespace MinecraftClient.Protocol.Handlers string registryName = dataTypes.ReadNextString(packetData); ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.forge_fml2_registry, registryName)); } - + fmlResponsePacket.AddRange(DataTypes.GetVarInt(99)); fmlResponseReady = true; break; @@ -442,7 +450,8 @@ 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 and greater + || ServerInfoCheckForgeSub(jsonData, ref forgeInfo, FMLVersion.FML3); // MC 1.18 and greater } /// @@ -464,7 +473,7 @@ namespace MinecraftClient.Protocol.Handlers { if (ServerMayForceForge(protocolVersion)) { - return new ForgeInfo(FMLVersion.FML2); + return new ForgeInfo(FMLVersion.FML3); } else throw new InvalidOperationException(Translations.error_forgeforce); } @@ -494,6 +503,11 @@ namespace MinecraftClient.Protocol.Handlers versionField = "fmlNetworkVersion"; versionString = "2"; break; + case FMLVersion.FML3: + forgeDataTag = "forgeData"; + versionField = "fmlNetworkVersion"; + versionString = "3"; + break; default: throw new NotImplementedException("FMLVersion '" + fmlVersion + "' not implemented!"); } @@ -523,6 +537,6 @@ namespace MinecraftClient.Protocol.Handlers } } return false; - } + } } }