2015-10-23 16:54:36 -07:00
using System ;
using System.Collections.Generic ;
namespace MinecraftClient.Protocol.Handlers.Forge
{
/// <summary>
/// Contains information about a modded server install.
/// </summary>
public class ForgeInfo
{
/// <summary>
/// Represents an individual forge mod.
/// </summary>
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 < ForgeMod > Mods ;
2020-08-11 12:52:38 +02:00
internal FMLVersion Version ;
2015-10-23 16:54:36 -07:00
2020-10-18 13:09:46 +02:00
/// <summary>
/// Create a new ForgeInfo with the given version.
/// </summary>
/// <param name="fmlVersion">FML version to use</param>
internal ForgeInfo ( FMLVersion fmlVersion )
{
switch ( fmlVersion )
{
case FMLVersion . FML2 :
2022-10-02 18:31:08 +08:00
Mods = new List < ForgeMod >
{
new ForgeMod ( "forge" , "ANY" )
} ;
Version = fmlVersion ;
2020-10-18 13:09:46 +02:00
break ;
2024-01-12 13:28:03 +08:00
case FMLVersion . FML3 :
Mods = new List < ForgeMod >
{
new ForgeMod ( "forge" , "ANY" )
} ;
Version = fmlVersion ;
break ;
2020-10-18 13:09:46 +02:00
default :
2022-10-28 11:13:20 +08:00
throw new InvalidOperationException ( Translations . error_forgeforce ) ;
2020-10-18 13:09:46 +02:00
}
}
2015-10-23 16:54:36 -07:00
/// <summary>
/// Create a new ForgeInfo from the given data.
/// </summary>
/// <param name="data">The modinfo JSON tag.</param>
2020-08-11 12:52:38 +02:00
/// <param name="fmlVersion">Forge protocol version</param>
internal ForgeInfo ( Json . JSONData data , FMLVersion fmlVersion )
2015-10-23 16:54:36 -07:00
{
2022-10-02 18:31:08 +08:00
Mods = new List < ForgeMod > ( ) ;
Version = fmlVersion ;
2020-08-08 22:08:37 +02:00
2020-08-11 12:52:38 +02:00
switch ( fmlVersion )
2020-08-08 22:08:37 +02:00
{
2020-08-11 12:52:38 +02:00
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 ;
2022-10-02 18:31:08 +08:00
Mods . Add ( new ForgeMod ( modid , modversion ) ) ;
2020-08-11 12:52:38 +02:00
}
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 ;
2022-10-02 18:31:08 +08:00
Mods . Add ( new ForgeMod ( modid , modmarker ) ) ;
2020-08-11 12:52:38 +02:00
}
break ;
2024-01-12 13:28:03 +08:00
case FMLVersion . FML3 :
2024-01-14 01:45:38 +08:00
// Example ModInfo for Minecraft 1.18 and greater (FML3)
2024-01-12 13:28:03 +08:00
// {
// "enforcesSecureChat": true,
// "forgeData": {
// "channels": [],
// "mods": [],
2024-01-14 01:45:38 +08:00
// "truncated": false, // legacy versions see truncated lists, modern versions ignore this truncated flag (binary data has its own)
2024-01-12 13:28:03 +08:00
// "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
// }
// }
2024-01-14 01:45:38 +08:00
// 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
2024-01-12 13:28:03 +08:00
string encodedData = data . Properties [ "d" ] . StringValue ;
Queue < byte > dataPackage = decodeOptimized ( encodedData ) ;
2024-01-14 01:45:38 +08:00
DataTypes dataTypes = new DataTypes ( Protocol18Handler . MC_1_18_1_Version ) ;
2024-01-12 13:28:03 +08:00
//
// [truncated][boolean] placeholder for whether we are truncating
// [Mod Size][unsigned short] short so that we can replace it later in case of truncation
//
2024-01-14 01:45:38 +08:00
bool truncated = dataTypes . ReadNextBool ( dataPackage ) ;
2024-01-12 13:28:03 +08:00
var modsSize = dataTypes . ReadNextUShort ( dataPackage ) ;
Dictionary < string , string > channels = new ( ) ;
Dictionary < string , string > mods = new ( ) ;
for ( var i = 0 ; i < modsSize ; i + + ) {
var channelSizeAndVersionFlag = dataTypes . ReadNextVarInt ( dataPackage ) ;
var channelSize = channelSizeAndVersionFlag > > 1 ;
int VERSION_FLAG_IGNORESERVERONLY = 0 b1 ;
var isIgnoreServerOnly = ( channelSizeAndVersionFlag & VERSION_FLAG_IGNORESERVERONLY ) ! = 0 ;
var modId = dataTypes . ReadNextString ( dataPackage ) ;
2024-01-14 01:45:38 +08:00
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";
2024-01-12 13:28:03 +08:00
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 ) ;
}
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 ) ;
}
break ;
2020-08-11 12:52:38 +02:00
default :
throw new NotImplementedException ( "FMLVersion '" + fmlVersion + "' not implemented!" ) ;
2020-08-08 22:08:37 +02:00
}
2015-10-23 16:54:36 -07:00
}
2024-01-12 13:28:03 +08:00
2024-01-14 01:45:38 +08:00
// https://github.com/MinecraftForge/MinecraftForge/blob/cb12df41e13da576b781be695f80728b9594c25f/src/main/java/net/minecraftforge/network/ServerStatusPing.java#L361
// Decode binary data ForgeData["d"] to Queue<byte>
private static Queue < byte > decodeOptimized ( string encodedData ) {
2024-01-12 13:28:03 +08:00
// Console.WriteLine("Got encoded data:" + encodedData + ", decoding...");
2024-01-14 01:45:38 +08:00
int size0 = encodedData [ 0 ] ;
int size1 = encodedData [ 1 ] ;
2024-01-12 13:28:03 +08:00
int size = size0 | ( size1 < < 15 ) ;
List < byte > 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 ] ;
2024-01-14 01:45:38 +08:00
buffer | = ( c & 0x7FFF ) < < bitsInBuf ;
2024-01-12 13:28:03 +08:00
bitsInBuf + = 15 ;
stringIndex + + ;
}
while ( packageData . Count < size )
{
packageData . Add ( ( byte ) buffer ) ;
buffer > > = 8 ;
bitsInBuf - = 8 ;
}
return new Queue < byte > ( packageData . ToArray ( ) ) ;
}
2015-10-23 16:54:36 -07:00
}
}