Minecraft-Console-Client/MinecraftClient/Mapping/BlockPalettes/BlockPaletteGenerator.cs
copilot-swe-agent[bot] 15aabd9423 Replace legacy custom JSON parser with System.Text.Json
- Rewrite Json.cs to use System.Text.Json.Nodes (JsonNode, JsonObject, JsonArray)
- Add JsonNodeExtensions.GetStringValue() for backward-compatible string access
- Update all 14 consumer files to use the new JsonNode API
- Remove ~300 lines of hand-rolled JSON parsing code from 2013
- Replace KeyUtils.EscapeString with delegation to Json.EscapeString

Co-authored-by: milutinke <441903+milutinke@users.noreply.github.com>
Agent-Logs-Url: https://github.com/milutinke/Minecraft-Console-Client/sessions/afcd1b7b-ea23-4a0d-bb46-a90b623406fc
2026-03-22 16:39:26 +00:00

159 lines
6.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
namespace MinecraftClient.Mapping.BlockPalettes
{
/// <summary>
/// Generator for MCC Block Palette mappings
/// </summary>
/// <remarks>
/// Example for generating MaterialXXX.cs and PaletteXXX.cs from blocks.json:
///
/// MinecraftClient.Mapping.BlockPalettes.BlockPaletteGenerator.JsonToClass(
/// @"C:\Path\To\blocks.json",
/// @"C:\Path\To\Output\PaletteXXX.cs",
/// @"C:\Path\To\Output\MaterialXXX.cs"
/// );
///
/// Place the above example inside the Main() method of Program.cs, adjust paths then compile and run.
/// Do not forget to remove the temporay call to JsonToClass() from Main() once you are done.
/// </remarks>
public static class BlockPaletteGenerator
{
/// <summary>
/// Generate mapping from Minecraft blocks.json
/// </summary>
/// <param name="blocksJsonFile">path to blocks.json</param>
/// <remarks>java -cp minecraft_server.jar net.minecraft.data.Main --reports</remarks>
/// <returns>state => block name mappings</returns>
public static void GenerateBlockPalette(string blocksJsonFile)
{
BlockPaletteGenerator.JsonToClass(blocksJsonFile, "Palette", "Material");
}
/// <summary>
/// Generate mapping from Minecraft blocks.json
/// </summary>
/// <param name="blocksJsonFile">path to blocks.json</param>
/// <param name="outputClass">output path for blocks.cs</param>
/// <param name="outputEnum">output path for material.cs</param>
/// <remarks>java -cp minecraft_server.jar net.minecraft.data.Main --reports</remarks>
/// <returns>state => block name mappings</returns>
public static void JsonToClass(string blocksJsonFile, string outputClass, string? outputEnum = null)
{
string outputPalettePath = Path.Combine(Path.GetDirectoryName(blocksJsonFile)!, outputClass + "XXX.cs");
string outputEnumPath = Path.Combine(Path.GetDirectoryName(blocksJsonFile)!, outputEnum + "XXX.cs");
HashSet<int> knownStates = new();
Dictionary<string, HashSet<int>> blocks = new();
var palette = Json.ParseJson(File.ReadAllText(blocksJsonFile, Encoding.UTF8))!.AsObject();
foreach (var item in palette)
{
//minecraft:item_name => ItemName
string blockType = String.Concat(
item.Key.Replace("minecraft:", "")
.Split('_')
.Select(word => char.ToUpper(word[0]) + word.Substring(1))
);
if (blocks.ContainsKey(blockType))
throw new InvalidDataException("Duplicate block type " + blockType + "!?");
blocks[blockType] = new HashSet<int>();
foreach (var state in item.Value!["states"]!.AsArray())
{
int id = int.Parse(state!["id"].GetStringValue(), NumberStyles.Any, CultureInfo.CurrentCulture);
if (knownStates.Contains(id))
throw new InvalidDataException("Duplicate state id " + id + "!?");
knownStates.Add(id);
blocks[blockType].Add(id);
}
}
HashSet<string> materials = new();
List<string> outFile = new();
outFile.AddRange(new[] {
"using System.Collections.Generic;",
"",
"namespace MinecraftClient.Mapping.BlockPalettes",
"{",
" public class PaletteXXX : BlockPalette",
" {",
" private static readonly Dictionary<int, Material> materials = new();",
"",
" static PaletteXXX()",
" {",
});
foreach (KeyValuePair<string, HashSet<int>> blockType in blocks)
{
if (blockType.Value.Count > 0)
{
List<int> idList = blockType.Value.ToList();
string materialName = blockType.Key;
materials.Add(materialName);
if (idList.Count > 1)
{
idList.Sort();
Queue<int> idQueue = new(idList);
while (idQueue.Count > 0)
{
int startValue = idQueue.Dequeue();
int endValue = startValue;
while (idQueue.Count > 0 && idQueue.Peek() == endValue + 1)
endValue = idQueue.Dequeue();
if (endValue > startValue)
{
outFile.Add(" for (int i = " + startValue + "; i <= " + endValue + "; i++)");
outFile.Add(" materials[i] = Material." + materialName + ";");
}
else outFile.Add(" materials[" + startValue + "] = Material." + materialName + ";");
}
}
else outFile.Add(" materials[" + idList[0] + "] = Material." + materialName + ";");
}
else throw new InvalidDataException("No state id for block type " + blockType.Key + "!?");
}
outFile.AddRange(new[] {
" }",
"",
" protected override Dictionary<int, Material> GetDict()",
" {",
" return materials;",
" }",
" }",
"}"
});
File.WriteAllLines(outputPalettePath, outFile);
if (outputEnum != null)
{
outFile = new List<string>();
outFile.AddRange(new[] {
"namespace MinecraftClient.Mapping",
"{",
" public enum Material",
" {"
});
foreach (string material in materials)
outFile.Add(" " + material + ",");
outFile.AddRange(new[] {
" }",
"}"
});
File.WriteAllLines(outputEnumPath, outFile);
}
}
}
}