Last active
December 19, 2025 04:46
-
-
Save zlrc/f7da49503ea7767b5814dfd3b0c7b66f to your computer and use it in GitHub Desktop.
Old export script for my Minecraft data pack, "Advance Your Health!". Generates overrides for every advancement in the game to run a function each time the player earns an advancement. This is single-purpose, but shouldn't be hard to modify into a more general script (see function: "create_advancement_override").
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Datapack Exporter by https://github.com/zlrc | |
| # Version: 1 | |
| # License: MIT | |
| # Derived from onnowhere's advancements generator script, licensed under CC0 for this specific version. | |
| # source: https://github.com/onnowhere/advancement-disabler/blob/fb50ae71ac454731c312fff2e6bf3d650c732d26/advancements_generator.py | |
| # license: https://github.com/onnowhere/advancement-disabler/blob/fb50ae71ac454731c312fff2e6bf3d650c732d26/LICENSE.md | |
| import os | |
| from shutil import rmtree, copy, copytree | |
| from zipfile import ZipFile | |
| import json | |
| import sys | |
| import errno | |
| def create_path(filepath : str): | |
| """Generates a directory at the given path, alias for os.makedirs but with race condition guarding.""" | |
| if not os.path.exists(filepath): | |
| try: | |
| os.makedirs(filepath) | |
| except OSError as exc: # Guard against race condition | |
| if exc.errno != errno.EEXIST: | |
| raise | |
| def create_file(filename : str, contents : str): | |
| """Generates a file with the given path and contents while avoiding race conditions.""" | |
| create_path(os.path.dirname(filename)) | |
| with open(filename, 'w', encoding='utf-8') as f: | |
| f.writelines(contents) | |
| def create_advancement_override(source_data : dict, out_filename : str): | |
| """ | |
| Creates a copy of the provided advancement data with a custom reward function appended to it. | |
| This override copy is immediately written to a new file. | |
| """ | |
| data = source_data | |
| if not "rewards" in data: | |
| data["rewards"] = {} | |
| if "function" in data["rewards"]: | |
| existing_fn = data["rewards"]["function"] | |
| print(f"'{os.path.basename(out_filename)}' already has a function defined: {existing_fn}") | |
| return | |
| data["rewards"]["function"] = "advance_your_health:increment_score" | |
| # Write to file | |
| create_file(out_filename, json.dumps(data, separators=(',', ':'))) | |
| def parse_mcmeta(path : str) -> object: | |
| """Reads and returns JSON data from the pack.mcmeta at the provided path.""" | |
| data = {} | |
| with open(path, "r") as mcmeta: | |
| data = json.load(mcmeta) | |
| if not "title" in data: | |
| data["title"] = "Untitled Datapack" | |
| if not "version" in data: | |
| data["version"] = "" | |
| return data | |
| def zip_files(dirname : str, zipname : str): | |
| """Generates a zip archive of the provided directory (dirname).""" | |
| path_length = len(dirname) | |
| with ZipFile(zipname, "w") as archive: | |
| # Iterate over all the files in directory | |
| for folderName, subfolders, filenames in os.walk(dirname): | |
| zipFolderName = folderName[path_length:] | |
| for filename in filenames: | |
| # Create complete filepath of file in directory | |
| filePath = os.path.join(folderName, filename) | |
| zipPath = os.path.join(zipFolderName, filename) | |
| # Add file to zip | |
| archive.write(filePath, zipPath) | |
| def generate_advancements(jar_file : str): | |
| """Generates a datapack with all advancements from the jar file overridden.""" | |
| jar_file = jar_file.strip('"') | |
| # Verify jar file | |
| if os.path.splitext(jar_file)[1] != ".jar": | |
| raise ValueError("Error: Invalid file type. Expected '.jar'.") | |
| # Create Directories | |
| mc_version = os.path.splitext(os.path.basename(jar_file))[0] | |
| mcmeta_path = "pack.mcmeta" | |
| pack_meta = parse_mcmeta(mcmeta_path) | |
| data_dir = "data" | |
| dist_dir = f"_dist/{pack_meta['title']}{' v' if pack_meta['version'] else ''}{pack_meta['version']} (MC {mc_version})" | |
| dist_zip = f"{dist_dir}.zip" | |
| advancements_dir = "data/minecraft/advancement" | |
| # Reset existing export directory | |
| if os.path.exists(dist_dir): | |
| rmtree(dist_dir) | |
| create_path(dist_dir) | |
| copy("pack.png", dist_dir) | |
| copy(mcmeta_path, dist_dir) | |
| copytree(data_dir, f"{dist_dir}/data") | |
| # Generate advancements | |
| with ZipFile(jar_file, "r") as archive: | |
| for file in archive.namelist(): | |
| if file.startswith(advancements_dir) and os.path.splitext(file)[1] == ".json" and not "recipes" in os.path.dirname(file): | |
| # Decode and modify advancement data | |
| with archive.open(file, "r") as json_file: | |
| data : dict = json.loads(json_file.read().decode("utf-8")) | |
| out_path : str = os.path.join(dist_dir, *file.split("/")) | |
| create_advancement_override(data, out_path) | |
| # Delete old zip | |
| if os.path.exists(dist_zip): | |
| os.remove(dist_zip) | |
| # Zip datapack files | |
| zip_files(dist_dir, dist_zip) | |
| # Cleanup: remove the unzipped directory | |
| rmtree(dist_dir) | |
| if __name__ == "__main__": | |
| try: | |
| jar_file = sys.argv[1] | |
| try: | |
| print("Generating advancements...") | |
| generate_advancements(jar_file) | |
| input("Finished generating. Press 'enter' or close this window to finish.") | |
| except: | |
| print("Stopped.") | |
| except: | |
| print("Welcome to the Datapack Exporter") | |
| print("NOTE: This will export your datapack with " + | |
| "\nseveral overwrite files for the vanilla advancements, " + | |
| "\nit is not ready for general-purpose exports yet.") | |
| while True: | |
| try: | |
| print("----------------------------------------------") | |
| print("Minecraft jar files can be found in '.minecraft/versions'.") | |
| jar_file = input("Drop Minecraft jar file here and hit enter: ") | |
| print("Generating advancements...") | |
| generate_advancements(jar_file) | |
| input("Finished generating. Press 'enter' to generate again or close this window to finish.") | |
| except KeyboardInterrupt as e: | |
| print(e) | |
| break | |
| except EOFError as e: | |
| print(e) | |
| break | |
| except Exception as e: | |
| print(e) | |
| print("Error encountered while generating. Please try again.") | |
| print("Shutting down...") | |
| sys.exit() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment