mirror of
https://github.com/ForeverPyrite/blast-furnace-extended.git
synced 2025-12-10 01:38:07 +00:00
142 lines
5.7 KiB
Python
142 lines
5.7 KiB
Python
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()
|