#!/usr/bin/env python3 """ Generate an MCC BlockPalette C# file from server-generated blocks.json. The blocks.json file is generated by running: java -DbundlerMainClass=net.minecraft.data.Main -jar server.jar --reports Usage: python3 tools/gen_block_palette.py Example: python3 tools/gen_block_palette.py /tmp/mc_reports/reports/blocks.json 1219 # Generates Palette1219.cs """ import json import re import sys from pathlib import Path OUTPUT_DIR = (Path(__file__).resolve().parent.parent / "MinecraftClient" / "Mapping" / "BlockPalettes") MATERIAL_CS = OUTPUT_DIR.parent / "Material.cs" def mc_name_to_csharp(mc_name: str) -> str: """Convert minecraft:snake_case to PascalCase C# enum name.""" name = mc_name.removeprefix("minecraft:") return "".join(word.capitalize() for word in name.split("_")) def load_known_materials() -> set[str]: known = set() if MATERIAL_CS.exists(): with open(MATERIAL_CS) as f: for line in f: m = re.match(r'\s+(\w+),?\s*$', line) if m: known.add(m.group(1)) return known def main(): if len(sys.argv) != 3: print(__doc__) sys.exit(1) blocks_json = Path(sys.argv[1]) class_suffix = sys.argv[2] if not blocks_json.exists(): print(f"Error: {blocks_json} not found") sys.exit(1) with open(blocks_json) as f: data = json.load(f) # Build (min_state, max_state, cs_name) for each block, sorted by min_state block_ranges = [] for block_key, block_info in data.items(): cs_name = mc_name_to_csharp(block_key) states = block_info.get("states", []) state_ids = [s["id"] for s in states] if state_ids: block_ranges.append((min(state_ids), max(state_ids), cs_name)) block_ranges.sort(key=lambda x: x[0]) print(f"Loaded {len(block_ranges)} blocks from {blocks_json}") max_state = max(r[1] for r in block_ranges) print(f"State ID range: 0 - {max_state}") known_materials = load_known_materials() missing = [cs for _, _, cs in block_ranges if known_materials and cs not in known_materials] if missing: print(f"\nWARNING: {len(missing)} blocks not found in Material.cs enum:") for cs_name in missing: print(f" {cs_name}") print("\nYou need to add these to Material.cs before the palette will compile.") print("Insert them in alphabetical order within the enum.") class_name = f"Palette{class_suffix}" output_path = OUTPUT_DIR / f"{class_name}.cs" lines = [ "using System.Collections.Generic;", "", "namespace MinecraftClient.Mapping.BlockPalettes", "{", f" public class {class_name} : BlockPalette", " {", " private static readonly Dictionary materials = new();", "", f" static {class_name}()", " {", ] for min_s, max_s, cs_name in block_ranges: lines.append(f" for (int i = {min_s}; i <= {max_s}; i++)") lines.append(f" materials[i] = Material.{cs_name};") lines += [ " }", "", " protected override Dictionary GetDict()", " {", " return materials;", " }", " }", "}", "", ] output_path.write_text("\n".join(lines)) print(f"Generated {output_path} with {len(block_ranges)} blocks ({max_state + 1} total states)") if __name__ == "__main__": main()