using System; using System.Collections.Generic; namespace MinecraftClient.Protocol.Handlers.Forge { /// /// Contains information about a modded server install. /// public class ForgeInfo { /// /// Represents an individual forge mod. /// public class ForgeMod { public ForgeMod(String ModID, String Version) { this.ModID = ModID; this.Version = Version; } public readonly String ModID; public readonly String Version; public override string ToString() { return ModID + " v" + Version; } } public List Mods; internal FMLVersion Version; /// /// Create a new ForgeInfo with the given version. /// /// FML version to use internal ForgeInfo(FMLVersion fmlVersion) { switch (fmlVersion) { case FMLVersion.FML2: Mods = new List { new ForgeMod("forge", "ANY") }; Version = fmlVersion; break; case FMLVersion.FML3: Mods = new List { new ForgeMod("forge", "ANY") }; Version = fmlVersion; break; default: throw new InvalidOperationException(Translations.error_forgeforce); } } /// /// Create a new ForgeInfo from the given data. /// /// The modinfo JSON tag. /// Forge protocol version internal ForgeInfo(Json.JSONData data, FMLVersion fmlVersion) { Mods = new List(); Version = fmlVersion; switch (fmlVersion) { case FMLVersion.FML: // Example ModInfo for Minecraft 1.12 and lower (FML) // "modinfo": { // "type": "FML", // "modList": [{ // "modid": "mcp", // "version": "9.05" // }, { // "modid": "FML", // "version": "8.0.99.99" // }, { // "modid": "Forge", // "version": "11.14.3.1512" // }, { // "modid": "rpcraft", // "version": "Beta 1.3 - 1.8.0" // }] // } foreach (Json.JSONData mod in data.Properties["modList"].DataArray) { String modid = mod.Properties["modid"].StringValue; String modversion = mod.Properties["version"].StringValue; Mods.Add(new ForgeMod(modid, modversion)); } break; case FMLVersion.FML2: // Example ModInfo for Minecraft 1.13 and greater (FML2) // "forgeData": { // "channels": [{ // "res": "minecraft:unregister", // "version": "FML2", // "required": true // }, { // "res": "minecraft:register", // "version": "FML2", // "required": true // }], // "mods": [{ // "modId": "minecraft", // "modmarker": "1.15.2" // }, { // "modId": "forge", // "modmarker": "ANY" // }, { // "modId": "rats", // "modmarker": "5.3.2" // }, { // "modId": "citadel", // "modmarker": "1.1.11" // }], // "fmlNetworkVersion": 2 // } foreach (Json.JSONData mod in data.Properties["mods"].DataArray) { String modid = mod.Properties["modId"].StringValue; String modmarker = mod.Properties["modmarker"].StringValue; Mods.Add(new ForgeMod(modid, modmarker)); } 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()); } } }