Minecraft-Console-Client/tools/gen_block_palette.py

120 lines
3.6 KiB
Python
Raw Normal View History

#!/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 <blocks.json> <suffix>
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<int, Material> 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<int, Material> 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()