#!/usr/bin/env python3 """Dumps the Minecraft API surface PhotoSync depends on, for every bucket. The adapters in :platform are written against exactly these signatures, and docs/PORTING.md's breakpoint table is generated from this output. Run it after adding a bucket to see, in one place, what that version changed: ./gradlew :platform::build # once, so Loom caches the jar python3 tools/probe-api.py > /tmp/api.txt """ import glob import os import re import subprocess import sys LOOM = os.path.expanduser("~/.gradle/caches/fabric-loom/minecraftMaven/net/minecraft") # Class, then the members worth watching. A name missing from the output is # itself the finding -- that is how the 26.x GuiGraphics removal showed up. TARGETS = [ (["net.minecraft.client.gui.GuiGraphics", "net.minecraft.client.gui.GuiGraphicsExtractor"], r"\b(fill|fillGradient|enableScissor|disableScissor|drawString|text|blit)\("), (["net.minecraft.client.gui.Font"], r"\b(width|lineHeight)\b"), (["net.minecraft.client.gui.components.events.GuiEventListener"], r"\b(mouseClicked|mouseReleased|mouseDragged|mouseScrolled|keyPressed|charTyped)\("), (["net.minecraft.client.gui.screens.Screen"], r"\b(render|extractRenderState|renderBackground|extractBackground|init|resize|removed" r"|isPauseScreen|shouldCloseOnEsc|tick)\("), (["net.minecraft.client.renderer.texture.DynamicTexture"], r"DynamicTexture\(|close\(|upload\(|getPixels\("), (["net.minecraft.client.renderer.texture.TextureManager"], r"\b(register|release|getTexture)\("), (["com.mojang.blaze3d.platform.NativeImage"], r"\b(read|writeToFile|setPixelRGBA|setPixel|close)\("), (["net.minecraft.client.Screenshot"], r"\b(grab|takeScreenshot|_grab)\("), # setScreen picks up setScreenAndShow too, which is the 26.x replacement. (["net.minecraft.client.Minecraft"], r"\b(stop|close|getInstance|setScreen|getWindow)\w*\("), (["net.minecraft.util.thread.BlockableEventLoop"], r"\b(execute|isSameThread)\("), (["net.minecraft.Util", "net.minecraft.util.Util"], r"\b(getPlatform|ioPool)\("), ] VERSION_KEY = re.compile(r"minecraft-merged-(\d+(?:\.\d+)*)-") def jars(): found = {} for jar in glob.glob(os.path.join(LOOM, "minecraft-merged", "*", "*.jar")): if "sources" in jar: continue match = VERSION_KEY.search(os.path.basename(jar)) if match: found.setdefault(match.group(1), jar) return sorted(found.items(), key=lambda item: [int(p) for p in item[0].split(".")]) def dump(jar, class_name, pattern): try: out = subprocess.run(["javap", "-cp", jar, "-p", class_name], capture_output=True, text=True, timeout=120) except FileNotFoundError: sys.exit("javap not on PATH; run this with a JDK available") if out.returncode != 0: return None return [line.strip() for line in out.stdout.splitlines() if re.search(pattern, line)] def main(): for version, jar in jars(): print("=" * 70) print(version) print("=" * 70) for class_names, pattern in TARGETS: for class_name in class_names: members = dump(jar, class_name, pattern) if members is None: continue print(f"\n-- {class_name}") for member in members: print(f" {member}") print() if __name__ == "__main__": main()