Files
2026-08-07 18:10:38 +00:00

85 lines
3.4 KiB
Python

#!/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:<version>: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()