#!/usr/bin/env python3 """Write the island world type from world/biomes.json. Vanilla's overworld biome parameters come from Minecraft's own data generator (run on the vanilla server jar that the NeoForge installer put in build/server; no server is started). Every entry is sent to its target biome from world/biomes.json, the extra entries are appended, and the result becomes the inline biome list of WorldGen Editor's delegate in datapacks/core/data/cobblesurvival/worldgen/world_preset/island.json. The world type has no Nether dimension. Usage: tools/biome_layout.py (run tools/smoke_test.py once first so build/server exists) """ import json import subprocess import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from mcserver import CACHE, PACK, ROOT, SERVER # noqa: E402 from packwiz_sync import java # noqa: E402 PRESET = ROOT / "datapacks/core/data/cobblesurvival/worldgen/world_preset/island.json" def vanilla_parameters(): version = PACK["versions"]["minecraft"] report = CACHE / f"datagen-{version}" / "reports/biome_parameters/minecraft/overworld.json" if not report.exists(): jar = SERVER / "libraries/net/minecraft/server" / version / f"server-{version}.jar" if not jar.exists(): sys.exit(f"{jar} is missing; run tools/smoke_test.py once so the NeoForge installer fetches it") work = CACHE / f"datagen-{version}" work.mkdir(parents=True, exist_ok=True) subprocess.run([java(), "-DbundlerMainClass=net.minecraft.data.Main", "-jar", str(jar), "--reports", "--output", str(work)], cwd=work, check=True, stdout=subprocess.DEVNULL) return json.loads(report.read_text())["biomes"] def main(): spec = json.loads((ROOT / "world/biomes.json").read_text()) targets = set(spec["allowed"]) | set(spec["water"]) entries, unmapped = [], set() for entry in vanilla_parameters(): target = spec["remap"].get(entry["biome"]) if target is None: unmapped.add(entry["biome"]) continue entries.append({"biome": target, "parameters": entry["parameters"]}) for extra in spec["add"]: entries.append({"biome": extra["biome"], "parameters": extra["parameters"]}) bad_targets = {e["biome"] for e in entries} - targets if unmapped or bad_targets: sys.exit(f"vanilla biomes without a target: {sorted(unmapped)}; targets outside the list: {sorted(bad_targets)}") preset = { "dimensions": { "minecraft:overworld": { "type": "minecraft:overworld", "generator": { "type": "minecraft:noise", "biome_source": { "type": "worldgen_editor:island_biome_source", "preset": "cobblesurvival_island", "delegate": {"type": "minecraft:multi_noise", "biomes": "@BIOMES@"}, }, "settings": "worldgen_editor:island_overworld", }, }, "minecraft:the_end": { "type": "minecraft:the_end", "generator": {"type": "minecraft:noise", "biome_source": {"type": "minecraft:the_end"}, "settings": "minecraft:end"}, }, } } # One biome entry per line keeps the 7,600-entry file diffable. body = ",\n".join(" " + json.dumps(e, separators=(",", ":")) for e in entries) text = json.dumps(preset, indent=2).replace('"@BIOMES@"', "[\n" + body + "\n ]") json.loads(text) PRESET.write_text(text + "\n") counts = {} for e in entries: counts[e["biome"]] = counts.get(e["biome"], 0) + 1 print(f"wrote {PRESET.relative_to(ROOT)}: {len(entries)} entries") for biome, n in sorted(counts.items(), key=lambda kv: -kv[1]): print(f" {n:5} {biome}") if __name__ == "__main__": main()