#!/usr/bin/env python3 """Measure Java 26.2 light properties using the verified local catalog cache.""" from pathlib import Path import argparse import hashlib import json import os import subprocess import zipfile ROOT = Path(__file__).resolve().parents[1] SOURCE_SHA1 = "823e2250d24b3ddac457a60c92a6a941943fcd6a" def digest(path, algorithm="sha256"): with path.open("rb") as source: return hashlib.file_digest(source, algorithm).hexdigest() def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--java", type=Path, required=True) parser.add_argument("--cache", type=Path, default=ROOT / "artifacts/catalog-cache") parser.add_argument("--output", type=Path, default=ROOT / "artifacts/lighting/reference.json") parser.add_argument("--runtime-output", type=Path, default=ROOT / "client/light-properties.json") parser.add_argument("--fixture-output", type=Path, default=ROOT / "client/tests/fixtures/lighting-java26.2.json") args = parser.parse_args() cache = args.cache.resolve() source = cache / "server.jar" if digest(source, "sha1") != SOURCE_SHA1: raise SystemExit("The official source JAR does not match pinned Java 26.2") executable = cache / "versions/26.2/server-26.2.jar" with zipfile.ZipFile(source) as bundle: with bundle.open("META-INF/versions/26.2/server-26.2.jar") as embedded: expected = hashlib.file_digest(embedded, "sha256").hexdigest() if digest(executable) != expected: raise SystemExit("The cached executable differs from the verified official bundle") classpath = os.pathsep.join(map(str, [executable, *sorted((cache / "libraries").rglob("*.jar"))])) java = args.java.resolve() script = ROOT / "scripts/lighting_reference.java" measured = cache / "lighting-measurements.json" commands = [ [str(java.with_name("javac")), "-cp", classpath, "-d", str(cache), str(script)], [str(java), "-Xmx1G", "-cp", str(cache) + os.pathsep + classpath, "lighting_reference", str(measured)], ] with (cache / "lighting-measurements.log").open("w") as log: for command in commands: result = subprocess.run(command, cwd=cache, stdout=log, stderr=subprocess.STDOUT) if result.returncode: raise SystemExit(f"Reference probe failed. Read {cache / 'lighting-measurements.log'}") reference = json.loads(measured.read_text()) reference["provenance"] = { "source_url": f"https://piston-data.mojang.com/v1/objects/{SOURCE_SHA1}/server.jar", "source_sha1": SOURCE_SHA1, "source_sha256": digest(source), "executable_sha256": digest(executable), "probe_sha256": digest(script), "command": "python3 scripts/measure_lighting.py --java /path/to/java25/bin/java", } args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(reference, separators=(",", ":")) + "\n") print(f"Wrote {args.output}: {len(reference['states'])} states, {len(reference['shapes'])} shapes, {args.output.stat().st_size} bytes") codes = [] for state in reference["states"]: state_id, dampening, shape_id = state[:3] assert state_id == len(codes), "Minecraft state IDs must be contiguous" assert 0 <= dampening <= 15 and 0 <= shape_id < len(reference["shapes"]) codes.append(dampening + 16 * shape_id) runtime = { "version": reference["version"], "encoding": "codes[minecraft_id] = light_dampening + 16 * occlusion_shape_index; blocks[name] = [first_state_id, default_state_id, schema_index]", "codes": codes, "shapes": reference["shapes"], "blocks": reference["blocks"], "schemas": reference["schemas"], "provenance": reference["provenance"], } args.runtime_output.parent.mkdir(parents=True, exist_ok=True) args.runtime_output.write_text(json.dumps(runtime, separators=(",", ":")) + "\n") print(f"Wrote {args.runtime_output}: {args.runtime_output.stat().st_size} bytes") fixture = {key: reference[key] for key in ("version", "examples", "crossings", "measurement_scope", "provenance")} args.fixture_output.parent.mkdir(parents=True, exist_ok=True) args.fixture_output.write_text(json.dumps(fixture, indent=2) + "\n") print(f"Wrote {args.fixture_output}: {len(fixture['crossings'])} original directional crossings") if __name__ == "__main__": main()