#!/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 ("remap"), or cut into pieces along one climate parameter ("split": each piece of the parameter's range goes to its own biome; the parts no piece covers are dropped, so the biome around them takes over), the extra entries are appended ("add"), 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 split(entry, rule): """The pieces of a vanilla entry along rule["parameter"], each sent to its own biome.""" name = rule["parameter"] value = entry["parameters"][name] low, high = (value, value) if isinstance(value, (int, float)) else value pieces = [] for piece in rule["pieces"]: a, b = max(low, piece["range"][0]), min(high, piece["range"][1]) if a < b or (a == b and low == high): pieces.append({"biome": piece["to"], "parameters": {**entry["parameters"], name: [a, b]}}) return pieces def main(): spec = json.loads((ROOT / "world/biomes.json").read_text()) targets = set(spec["allowed"]) | set(spec["water"]) entries, unmapped = [], set() splits = {s["biome"]: s for s in spec.get("split", [])} for entry in vanilla_parameters(): if entry["biome"] in splits: entries += split(entry, splits[entry["biome"]]) continue 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()