Inital commit.

Publishing this to GitHub cause I've forgotten to for so long.
This commit is contained in:
foreverpyrite
2025-11-09 15:44:11 -06:00
commit 9f94879072
4 changed files with 201 additions and 0 deletions

141
generate_recipes.py Normal file
View File

@@ -0,0 +1,141 @@
import json
from os.path import basename
import zipfile
import shutil
from pathlib import Path
# --- Configuration ---
# The path to the Minecraft server .jar file.
# You'll need to download this for the version you want to target.
# You can usually find it on the official Minecraft website.
JAR_PATH = Path("server.jar")
# The directory where the generated datapack files will be placed.
DATAPACK_DIR = Path("data")
# --- Script ---
def get_recipes_from_jar(
jar_path: Path, recipe_type: str = "smelting"
) -> dict[str, dict[str, str]]:
"""Extracts all recipes of a specific type from the Minecraft server jar."""
recipes: dict[str, dict[str, str]] = {}
with zipfile.ZipFile(jar_path, "r") as jar:
for filename in jar.namelist():
if filename.startswith("data/minecraft/recipe/") and filename.endswith(
".json"
):
with jar.open(filename) as recipe_file:
try:
recipe_data = json.load(recipe_file)
if recipe_data.get("type") == f"minecraft:{recipe_type}":
# Use the filename (without extension) as the recipe ID
recipe_id = Path(filename).stem
recipes[recipe_id] = recipe_data
except (json.JSONDecodeError, UnicodeDecodeError):
# This handles cases where a file isn't valid JSON, which can happen.
print(f"Warning: Could not parse {filename}, skipping.")
continue
return recipes
def convert_to_blasting(smelting_recipe: dict[str, str | int]) -> dict[str, str | int]:
"""Converts a smelting recipe to a blasting recipe."""
blasting_recipe = smelting_recipe.copy()
blasting_recipe["type"] = "minecraft:blasting"
# Blasting is twice as fast as smelting
# gotta type check it to get pyright to shut up...
if "cookingtime" in blasting_recipe and isinstance(
blasting_recipe["cookingtime"], int
):
blasting_recipe["cookingtime"] = int(blasting_recipe["cookingtime"] / 2)
else:
# Default smelting time is 200 ticks
blasting_recipe["cookingtime"] = 100
return blasting_recipe
def get_server_jar_from_jar(jar_path: Path) -> Path | None:
with zipfile.ZipFile(jar_path, "r") as jar:
for filename in jar.namelist():
if filename.startswith("META-INF/versions/") and filename.endswith(".jar"):
jar.extract(filename, basename(filename))
return Path(basename(filename) + "/" + filename)
return None
def main():
"""Main function to generate the blasting recipes."""
print("Starting recipe generation...")
if not Path(JAR_PATH).exists():
print("Error: The server file was not found.")
print("Please download the Minecraft server .jar for the version you want,")
print(
f"rename it to '{JAR_PATH}', and place it in the same directory as this script."
)
return
server_jar_path = get_server_jar_from_jar(JAR_PATH)
if server_jar_path is None:
print(
"Error: Unable to pull server jar out of server jar. (yes, you read that right)"
)
print("Are you sure you downloaded a proper server jar?")
return
print("Loading recipes from the server jar...")
smelting_recipes = get_recipes_from_jar(server_jar_path, "smelting")
blasting_recipes = get_recipes_from_jar(server_jar_path, "blasting")
smoking_recipes = get_recipes_from_jar(server_jar_path, "smoking")
print(f"Found {len(smelting_recipes)} smelting recipes.")
print(f"Found {len(blasting_recipes)} vanilla blasting recipes.")
print(f"Found {len(smoking_recipes)} vanilla smoking recipes.")
# Determine which smelting recipes need a blasting equivalent
# We do this by comparing the output item of the recipes
blasting_results = [recipe.get("result") for recipe in blasting_recipes.values()]
# Exclude recipes that are already handled by the smoker.
smoking_results = [recipe.get("result") for recipe in smoking_recipes.values()]
# Holy list comprehension
recipes_to_create = {
name: recipe
for name, recipe in smelting_recipes.items()
if recipe.get("result") not in blasting_results
and recipe.get("result") not in smoking_results
}
print(
f"\nFound {len(recipes_to_create)} smelting recipes to convert to blasting (after excluding existing blasting/smoking recipes)."
)
# Clean up old recipes and create the directory structure
# Using 'recipe' as the path, per your correction.
recipes_path = DATAPACK_DIR / "minecraft" / "recipe"
if recipes_path.exists():
shutil.rmtree(recipes_path)
recipes_path.mkdir(parents=True, exist_ok=True)
print(f"Cleaned and created directory: {recipes_path}")
# Generate the new .json files
for name, recipe in recipes_to_create.items():
blasting_recipe = convert_to_blasting(recipe)
# To avoid any potential conflicts, we'll add a suffix to the name
output_filename = recipes_path / f"{name}_from_blasting.json"
with open(output_filename, "w") as f:
json.dump(blasting_recipe, f, indent=2)
print(f"\nSuccessfully generated {len(recipes_to_create)} new blasting recipes!")
print(f"They have been saved in: {recipes_path}")
print("\nNext steps:")
print(
"1. Make sure your `pack.mcmeta` file has the correct `pack_format` for the Minecraft version you are targeting."
)
print(
"2. Zip the `data` directory and the `pack.mcmeta` file to create your distributable datapack."
)
if __name__ == "__main__":
main()