From 809bfddd32ba861150dfb35ca38ffbd3233406c8 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 12 Apr 2023 16:59:55 +0200 Subject: [PATCH] [Change] Recipe Handling moved to Datagen --- .../api/v3/datagen/DatapackRecipeBuilder.java | 15 ++ .../api/v3/datagen/RecipeDataProvider.java | 41 +++ .../WoodenComplexMaterial.java | 4 +- .../items/complex/EquipmentDescription.java | 4 +- .../recipes/AbstractAdvancementRecipe.java | 220 ---------------- .../recipes/AbstractBaseRecipeBuilder.java | 173 ++++++++++++ .../AbstractDoubleInputRecipeBuilder.java | 79 ++++++ .../bclib/recipes/AbstractSimpleRecipe.java | 102 ------- .../recipes/AbstractSimpleRecipeBuilder.java | 57 ++++ .../AbstractSingleInputRecipeBuilder.java | 113 ++++++++ .../AbstractUnlockableRecipeBuilder.java | 35 +++ .../betterx/bclib/recipes/AlloyingRecipe.java | 147 ++++------- .../betterx/bclib/recipes/AnvilRecipe.java | 143 +++++----- .../bclib/recipes/BCLRecipeBuilder.java | 20 +- .../bclib/recipes/BlastFurnaceRecipe.java | 37 --- .../betterx/bclib/recipes/CookingRecipe.java | 50 ---- .../bclib/recipes/CookingRecipeBuilder.java | 208 +++++++++++++++ .../bclib/recipes/CraftingRecipeBuilder.java | 225 ++++++++++++++++ .../betterx/bclib/recipes/FurnaceRecipe.java | 157 ----------- .../org/betterx/bclib/recipes/GridRecipe.java | 249 ------------------ .../bclib/recipes/SmithingRecipeBuilder.java | 77 ++++++ .../bclib/recipes/SmithingTableRecipe.java | 64 ----- .../bclib/recipes/StoneCutterRecipe.java | 35 --- .../recipes/StonecutterRecipeBuilder.java | 67 +++++ .../java/org/betterx/bclib/util/ItemUtil.java | 57 ++++ .../org/betterx/bclib/util/JsonFactory.java | 1 + .../datagen/bclib/tests/TestRecipes.java | 18 +- src/main/resources/fabric.mod.json | 2 +- 28 files changed, 1279 insertions(+), 1121 deletions(-) create mode 100644 src/main/java/org/betterx/bclib/api/v3/datagen/DatapackRecipeBuilder.java create mode 100644 src/main/java/org/betterx/bclib/api/v3/datagen/RecipeDataProvider.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractAdvancementRecipe.java create mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractBaseRecipeBuilder.java create mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractDoubleInputRecipeBuilder.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipe.java create mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipeBuilder.java create mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractSingleInputRecipeBuilder.java create mode 100644 src/main/java/org/betterx/bclib/recipes/AbstractUnlockableRecipeBuilder.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/BlastFurnaceRecipe.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/CookingRecipe.java create mode 100644 src/main/java/org/betterx/bclib/recipes/CookingRecipeBuilder.java create mode 100644 src/main/java/org/betterx/bclib/recipes/CraftingRecipeBuilder.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/GridRecipe.java create mode 100644 src/main/java/org/betterx/bclib/recipes/SmithingRecipeBuilder.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java delete mode 100644 src/main/java/org/betterx/bclib/recipes/StoneCutterRecipe.java create mode 100644 src/main/java/org/betterx/bclib/recipes/StonecutterRecipeBuilder.java diff --git a/src/main/java/org/betterx/bclib/api/v3/datagen/DatapackRecipeBuilder.java b/src/main/java/org/betterx/bclib/api/v3/datagen/DatapackRecipeBuilder.java new file mode 100644 index 00000000..d162d665 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/datagen/DatapackRecipeBuilder.java @@ -0,0 +1,15 @@ +package org.betterx.bclib.api.v3.datagen; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.resources.ResourceLocation; + +import java.util.function.Consumer; + +public interface DatapackRecipeBuilder { + ResourceLocation getId(); + + default String getNamespace() { + return this.getId().getNamespace(); + } + void build(Consumer cc); +} diff --git a/src/main/java/org/betterx/bclib/api/v3/datagen/RecipeDataProvider.java b/src/main/java/org/betterx/bclib/api/v3/datagen/RecipeDataProvider.java new file mode 100644 index 00000000..158fbf61 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/datagen/RecipeDataProvider.java @@ -0,0 +1,41 @@ +package org.betterx.bclib.api.v3.datagen; + +import net.minecraft.data.recipes.FinishedRecipe; + +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +public class RecipeDataProvider extends FabricRecipeProvider { + private static List RECIPES; + + @Nullable + protected final List modIDs; + + public RecipeDataProvider(@Nullable List modIDs, FabricDataOutput output) { + super(output); + this.modIDs = modIDs; + } + + @Override + public void buildRecipes(Consumer exporter) { + if (RECIPES == null) return; + + for (var r : RECIPES) { + if (modIDs.size() == 0 || modIDs.indexOf(r.getNamespace()) >= 0) { + r.build(exporter); + } + } + } + + @ApiStatus.Internal + public static void register(DatapackRecipeBuilder builder) { + if (RECIPES == null) RECIPES = new ArrayList<>(); + RECIPES.add(builder); + } +} diff --git a/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java b/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java index 0b99681f..59220e62 100644 --- a/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java +++ b/src/main/java/org/betterx/bclib/complexmaterials/WoodenComplexMaterial.java @@ -270,7 +270,7 @@ public class WoodenComplexMaterial extends ComplexMaterial { BCLRecipeBuilder.crafting(id, planks) .checkConfig(config) .setOutputCount(4) - .setList("#") + .shapeless() .addMaterial('#', log, bark, log_stripped, bark_stripped) .setGroup(receipGroupPrefix + "_planks") .build(); @@ -315,7 +315,7 @@ public class WoodenComplexMaterial extends ComplexMaterial { addRecipeEntry(new RecipeEntry("button", (material, config, id) -> { BCLRecipeBuilder.crafting(id, getBlock(BLOCK_BUTTON)) .checkConfig(config) - .setList("#") + .shapeless() .addMaterial('#', planks) .setGroup(receipGroupPrefix + "_planks_buttons") .build(); diff --git a/src/main/java/org/betterx/bclib/items/complex/EquipmentDescription.java b/src/main/java/org/betterx/bclib/items/complex/EquipmentDescription.java index 39538428..5229d39c 100644 --- a/src/main/java/org/betterx/bclib/items/complex/EquipmentDescription.java +++ b/src/main/java/org/betterx/bclib/items/complex/EquipmentDescription.java @@ -2,7 +2,7 @@ package org.betterx.bclib.items.complex; import org.betterx.bclib.items.BaseArmorItem; import org.betterx.bclib.recipes.BCLRecipeBuilder; -import org.betterx.bclib.recipes.GridRecipe; +import org.betterx.bclib.recipes.CraftingRecipeBuilder; import org.betterx.bclib.registry.ItemRegistry; import net.minecraft.resources.ResourceLocation; @@ -45,7 +45,7 @@ public class EquipmentDescription { } - protected boolean buildRecipe(Item tool, ItemLike stick, GridRecipe builder) { + protected boolean buildRecipe(Item tool, ItemLike stick, CraftingRecipeBuilder builder) { if (tool instanceof ShearsItem) { builder.setShape(" #", "# "); } else if (tool instanceof BaseArmorItem bai) { diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractAdvancementRecipe.java b/src/main/java/org/betterx/bclib/recipes/AbstractAdvancementRecipe.java deleted file mode 100644 index 6765a751..00000000 --- a/src/main/java/org/betterx/bclib/recipes/AbstractAdvancementRecipe.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.betterx.bclib.recipes; - -import org.betterx.bclib.api.v2.advancement.AdvancementManager; - -import net.minecraft.advancements.RequirementsStrategy; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TieredItem; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.level.ItemLike; -import net.minecraft.world.level.block.Block; - -import java.util.Arrays; -import java.util.stream.Collectors; - -/** - * Simple extension class that allows Recipe-builders to automatically generated Recipe-Avancements. - *

- * Implementing classes need to call {@link #createAdvancement(ResourceLocation, boolean)} or - * {@link #createAdvancement(ResourceLocation, ItemLike)} to enable an advncement and - * {@link #registerAdvancement(Recipe)} to finalize and register the new Advancement. - *

- * After that the unlockedBy-Methods can bes used to add Items that will unlock the Recipe (and prompt the unlock) - */ -public class AbstractAdvancementRecipe { - protected AdvancementManager.Builder advancement; - boolean hasUnlockTrigger = false; - boolean generateAdvancement = false; - - /** - * Your implementing class should call this method to prepare a new {@link AdvancementManager.Builder} - *

- * For Example {@link FurnaceRecipe} will call this in the - * {@link FurnaceRecipe#make(ResourceLocation, ItemLike)}-Method - * - * @param id {@link ResourceLocation} for this advancement - * @param isTool true, if this is registered for a tool - */ - protected void createAdvancement(ResourceLocation id, boolean isTool) { - hasUnlockTrigger = false; - generateAdvancement = true; - advancement = AdvancementManager.Builder.create( - id, - isTool - ? AdvancementManager.AdvancementType.RECIPE_TOOL - : AdvancementManager.AdvancementType.RECIPE_DECORATIONS - ); - } - - - /** - * Your implementing class should call this method to prepare a new {@link AdvancementManager.Builder} - *

- * For Example {@link GridRecipe} will call this in the {@link GridRecipe#make(ResourceLocation, ItemLike)}-Method. - *

- * This method will call {@link #createAdvancement(ResourceLocation, boolean)}. The output object is used to - * determine wether or not this is a tool recipe. - * - * @param id {@link ResourceLocation} for this advancement - * @param output Used to determine wether or not this is a tool recipe. - */ - protected void createAdvancement(ResourceLocation id, ItemLike output) { - createAdvancement(id, (output.asItem() instanceof TieredItem || output.asItem() instanceof ArmorItem)); - } - - private int nameCounter = 0; - - /** - * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory - * the recipe will unlock. Those Items are mostly the input Items for the recipe. - *

- * If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance - * using {@link #getAdvancementBuilder()} - *

- * This method will automatically get the Items from the stacl and call {@link #unlockedBy(ItemLike...)} - * - * @param stacks {@link ItemStack}s that will unlock the recipe. The count is ignored. - */ - public void unlockedBy(ItemStack... stacks) { - ItemLike[] items = Arrays.stream(stacks) - .filter(stack -> stack.getCount() > 0) - .map(stack -> (ItemLike) stack.getItem()) - .toArray(ItemLike[]::new); - - unlockedBy(items); - } - - /** - * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory - * the recipe will unlock. Those Items are mostly the input Items for the recipe. - *

- * If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance - * using {@link #getAdvancementBuilder()} - *

- * This method will automatically derive a unique name for the criterion and call - * {@link #unlockedBy(String, ItemLike...)} - * - * @param items {@link Item}s or {@link Block}s that will unlock the recipe. - */ - public void unlockedBy(ItemLike... items) { - String name = "has_" + (nameCounter++) + "_" + - Arrays.stream(items) - .map(block -> (block instanceof Block) - ? BuiltInRegistries.BLOCK.getKey((Block) block) - : BuiltInRegistries.ITEM.getKey((Item) block)) - .filter(id -> id != null) - .map(id -> id.getPath()) - .collect(Collectors.joining("_")); - if (name.length() > 45) name = name.substring(0, 42); - unlockedBy(name, items); - } - - /** - * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory - * the recipe will unlock. Those Items are mostly the input Items for the recipe. - *

- * If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance - * using {@link #getAdvancementBuilder()} - *

- * This method will automatically derive a unique name for the criterion and call - * {@link #unlockedBy(String, TagKey)} - * - * @param tag All items from this Tag will unlock the recipe - */ - public void unlockedBy(TagKey tag) { - ResourceLocation id = tag.location(); - if (id != null) { - unlockedBy("has_tag_" + id.getPath(), tag); - } - } - - - /** - * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory - * the recipe will unlock. Those Items are mostly the input Items for the recipe. - *

- * If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance - * using {@link #getAdvancementBuilder()} - * - * @param name The name for this unlock-Criteria - * @param items {@link Item}s or {@link Block}s that will unlock the recipe. - */ - public void unlockedBy(String name, ItemLike... items) { - if (advancement != null) { - hasUnlockTrigger = true; - advancement.addInventoryChangedCriterion(name, items); - } - } - - /** - * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory - * the recipe will unlock. Those Items are mostly the input Items for the recipe. - *

- * If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance - * using {@link #getAdvancementBuilder()} - * - * @param name The name for this unlock-Criteria - * @param tag All items from this Tag will unlock the recipe - */ - public void unlockedBy(String name, TagKey tag) { - if (advancement != null) { - hasUnlockTrigger = true; - advancement.addInventoryChangedCriterion(name, tag); - } - } - - /** - * Disables the generation of an advancement - */ - public void noAdvancement() { - generateAdvancement = false; - } - - /** - * Resets the advancement. This is usefully when you need more controll but the Recipe Builder - * (like {@link GridRecipe} is automatically adding Items to the criterion list. - */ - public void resetAdvancement() { - if (advancement != null) - advancement = AdvancementManager.Builder.createEmptyCopy(advancement); - } - - /** - * Returns the underlying Builder Instance - * - * @return null or The underlying builder Instance - */ - public AdvancementManager.Builder getAdvancementBuilder() { - return advancement; - } - - - /** - * Your implementing class should calls this method to finalize the advancement and register it. - *

- * For Example {@link GridRecipe} will call this in the {@link GridRecipe#build()}-Method - * - * @param recipe The generated recipe that need to be linked to the Advancement - */ - protected void registerAdvancement(Recipe recipe, ItemLike icon) { - if (hasUnlockTrigger && generateAdvancement && advancement != null) { - advancement - .startDisplay(icon) - .hideFromChat() - .hideToast() - .endDisplay() - .addRecipeUnlockCriterion("has_the_recipe", recipe) - .startReward() - .addRecipe(recipe.getId()) - .endReward() - .requirements(RequirementsStrategy.OR); - - advancement.build(); - } - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractBaseRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/AbstractBaseRecipeBuilder.java new file mode 100644 index 00000000..8a8820f8 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AbstractBaseRecipeBuilder.java @@ -0,0 +1,173 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.v3.datagen.DatapackRecipeBuilder; +import org.betterx.bclib.api.v3.datagen.RecipeDataProvider; +import org.betterx.bclib.util.RecipeHelper; + +import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.critereon.InventoryChangeTrigger; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; + +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public abstract class AbstractBaseRecipeBuilder implements DatapackRecipeBuilder { + protected final ResourceLocation id; + protected final ItemStack output; + protected String group; + + protected RecipeCategory category; + + protected boolean alright; + + + protected AbstractBaseRecipeBuilder(ResourceLocation id, ItemLike output) { + this.id = id; + this.output = new ItemStack(output, 1); + this.category = RecipeCategory.MISC; + this.alright = RecipeHelper.exists(output); + } + + public T setCategory(RecipeCategory category) { + this.category = category; + return (T) this; + } + + protected T setGroup(String group) { + this.group = group; + return (T) this; + } + + protected T setOutputCount(int count) { + this.output.setCount(count); + return (T) this; + } + + protected T setOutputTag(CompoundTag tag) { + this.output.setTag(tag); + return (T) this; + } + + protected T unlockedBy(ItemLike item) { + this.unlocks( + "has_" + item.asItem().getDescriptionId(), + RecipeProvider.has(item.asItem()) + ); + + return (T) this; + } + + protected T unlockedBy(TagKey tag) { + this.unlocks( + "has_tag_" + tag.location().getNamespace() + "_" + tag.location().getPath(), + RecipeProvider.has(tag) + ); + + return (T) this; + } + + /** + * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory + * the recipe will unlock. Those Items are mostly the input Items for the recipe. + *

+ * This method will automatically derive a unique name for the criterion and call + * {@link #unlocks(String, ItemLike...)} + * + * @param items {@link Item}s or {@link Block}s that will unlock the recipe. + */ + protected T unlockedBy(ItemLike... items) { + String name = "has_" + + Arrays.stream(items) + .map(block -> (block instanceof Block) + ? BuiltInRegistries.BLOCK.getKey((Block) block) + : BuiltInRegistries.ITEM.getKey((Item) block)) + .filter(id -> id != null) + .map(id -> id.getPath()) + .collect(Collectors.joining("_")); + if (name.length() > 45) name = name.substring(0, 42); + return unlocks(name, items); + } + + /** + * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory + * the recipe will unlock. Those Items are mostly the input Items for the recipe. + * + * @param name The name for this unlock-Criteria + * @param items {@link Item}s or {@link Block}s that will unlock the recipe. + */ + protected T unlocks(String name, ItemLike... items) { + return unlocks(name, InventoryChangeTrigger.TriggerInstance.hasItems(items)); + } + + /** + * The Recipe will be unlocked by one of the passed Items. As sonn als players have one in their Inventory + * the recipe will unlock. Those Items are mostly the input Items for the recipe. + *

+ * This method will automatically get the Items from the stacl and call {@link #unlockedBy(ItemLike...)} + * + * @param stacks {@link ItemStack}s that will unlock the recipe. The count is ignored. + */ + protected T unlockedBy(ItemStack... stacks) { + ItemLike[] items = Arrays.stream(stacks) + .filter(stack -> stack.getCount() > 0) + .map(stack -> (ItemLike) stack.getItem()) + .toArray(ItemLike[]::new); + + return unlockedBy(items); + } + + protected abstract T unlocks(String name, CriterionTriggerInstance trigger); + + + public final T build() { + if (!checkRecipe()) + return (T) this; + if (id.toString().equals("betternether:mushroom_fir_taburet")) { + System.out.println("betternether:mushroom_fir_taburet"); + } + RecipeDataProvider.register(this); + return (T) this; + } + + protected boolean checkRecipe() { + if (output == null) { + BCLib.LOGGER.warning("Output for Recipe can't be 'null', recipe {} will be ignored!", id); + return false; + } + if (output.is(Items.AIR)) { + BCLib.LOGGER.warning("Unable to build Recipe " + id + ": Result is AIR"); + return false; + } + if (!alright) { + BCLib.LOGGER.debug("Can't add recipe {}! Ingeredient or output do not exists.", id); + return false; + } + return true; + } + + protected abstract void buildRecipe(Consumer cc); + + @Override + public final void build(Consumer cc) { + if (!checkRecipe()) return; + buildRecipe(cc); + } + + @Override + public final ResourceLocation getId() { + return id; + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractDoubleInputRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/AbstractDoubleInputRecipeBuilder.java new file mode 100644 index 00000000..a3a56c1e --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AbstractDoubleInputRecipeBuilder.java @@ -0,0 +1,79 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.util.ItemUtil; +import org.betterx.bclib.util.RecipeHelper; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.level.ItemLike; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public abstract class AbstractDoubleInputRecipeBuilder> extends AbstractSingleInputRecipeBuilder { + protected Ingredient secondaryInput; + + protected AbstractDoubleInputRecipeBuilder( + ResourceLocation id, + ItemLike output + ) { + super(id, output); + } + + public T setSecondaryInput(ItemLike... inputs) { + for (ItemLike item : inputs) { + this.alright &= RecipeHelper.exists(item); + } + this.secondaryInput = Ingredient.of(inputs); + return (T) this; + } + + public T setSecondaryInput(TagKey input) { + this.secondaryInput = Ingredient.of(input); + return (T) this; + } + + public T setSecondaryInputAndUnlock(TagKey input) { + setPrimaryInput(input); + this.unlockedBy(input); + return (T) this; + } + + public T setSecondaryInputAndUnlock(ItemLike... inputs) { + setSecondaryInput(inputs); + for (ItemLike item : inputs) unlockedBy(item); + + return (T) this; + } + + @Override + protected boolean checkRecipe() { + if (secondaryInput == null) { + BCLib.LOGGER.warning( + "Secondary input for Recipe can't be 'null', recipe {} will be ignored!", + id + ); + return false; + } + return super.checkRecipe(); + } + + @Override + protected void serializeRecipeData(JsonObject root) { + final JsonArray ingredients = new JsonArray(); + ingredients.add(primaryInput.toJson()); + ingredients.add(secondaryInput.toJson()); + root.add("ingredients", ingredients); + + if (group != null && !group.isEmpty()) { + root.addProperty("group", group); + } + + root.add("result", ItemUtil.toJsonRecipe(output)); + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipe.java b/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipe.java deleted file mode 100644 index 6107d981..00000000 --- a/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipe.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.betterx.bclib.recipes; - -import org.betterx.bclib.BCLib; -import org.betterx.bclib.config.PathConfig; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.ItemLike; - -public abstract class AbstractSimpleRecipe> extends AbstractAdvancementRecipe { - public final ResourceLocation id; - protected String group; - protected Ingredient input; - protected ItemLike output; - protected final String category; - protected final RecipeType type; - protected int count; - protected boolean exist; - - - protected AbstractSimpleRecipe(ResourceLocation id, RecipeType type, ItemLike output) { - this(id, type, type.toString(), output); - } - - protected AbstractSimpleRecipe(ResourceLocation id, RecipeType type, String category, ItemLike output) { - this.id = id; - this.group = ""; - this.exist = true; - this.count = 1; - this.output = output; - - this.category = category; - this.type = type; - } - - - protected T setInput(ItemLike in) { - this.exist &= BCLRecipeManager.exists(in); - this.input = Ingredient.of(in); - unlockedBy(in); - return (T) this; - } - - protected T setInput(TagKey in) { - this.input = Ingredient.of(in); - unlockedBy(in); - return (T) this; - } - - public T setGroup(String group) { - this.group = group; - return (T) this; - } - - public T setOutputCount(int count) { - this.count = count; - return (T) this; - } - - public T checkConfig(PathConfig config) { - exist &= config.getBoolean(category, id.getPath(), true); - return (T) this; - } - - protected abstract R buildRecipe(); - - protected boolean hasErrors() { - return false; - } - - public final void build() { - if (!exist) { - BCLib.LOGGER.warning("Unable to build Recipe " + id); - return; - } - - if (input == null || input.isEmpty()) { - BCLib.LOGGER.warning("Unable to build Recipe " + id + ": No Input Material"); - return; - } - - if (output == null) { - BCLib.LOGGER.warning("Result recipe can't be 'null', recipe {} will be ignored!", id); - return; - } - if (BCLRecipeManager.getRecipe(type, id) != null) { - BCLib.LOGGER.warning("Can't add Smithing recipe! Id {} already exists!", id); - return; - } - - if (hasErrors()) return; - - R recipe = buildRecipe(); - BCLRecipeManager.addRecipe(type, recipe); - registerAdvancement(recipe, output); - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipeBuilder.java new file mode 100644 index 00000000..4c5fbbba --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AbstractSimpleRecipeBuilder.java @@ -0,0 +1,57 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.util.RecipeHelper; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; + +public abstract class AbstractSimpleRecipeBuilder extends AbstractBaseRecipeBuilder { + protected Ingredient primaryInput; + + protected AbstractSimpleRecipeBuilder(ResourceLocation id, ItemLike output) { + super(id, output); + } + + + public T setPrimaryInput(ItemLike... inputs) { + for (ItemLike item : inputs) { + this.alright &= RecipeHelper.exists(item); + } + this.primaryInput = Ingredient.of(inputs); + return (T) this; + } + + public T setPrimaryInput(TagKey input) { + this.primaryInput = Ingredient.of(input); + return (T) this; + } + + public T setPrimaryInputAndUnlock(TagKey input) { + setPrimaryInput(input); + this.unlockedBy(input); + return (T) this; + } + + public T setPrimaryInputAndUnlock(ItemLike... inputs) { + setPrimaryInput(inputs); + for (ItemLike item : inputs) unlockedBy(item); + + return (T) this; + } + + + protected boolean checkRecipe() { + if (primaryInput == null) { + BCLib.LOGGER.warning( + "Primary input for Recipe can't be 'null', recipe {} will be ignored!", + id + ); + return false; + } + return super.checkRecipe(); + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractSingleInputRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/AbstractSingleInputRecipeBuilder.java new file mode 100644 index 00000000..115d2ddb --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AbstractSingleInputRecipeBuilder.java @@ -0,0 +1,113 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.util.ItemUtil; + +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.advancements.RequirementsStrategy; +import net.minecraft.advancements.critereon.RecipeUnlockedTrigger; +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.RecipeBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.level.ItemLike; + +import com.google.gson.JsonObject; + +import java.util.function.Consumer; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractSingleInputRecipeBuilder> extends AbstractSimpleRecipeBuilder { + protected final Advancement.Builder advancement; + + + protected AbstractSingleInputRecipeBuilder(ResourceLocation id, ItemLike output) { + super(id, output); + this.advancement = Advancement.Builder.advancement(); + } + + @Override + public T unlockedBy(ItemLike item) { + return super.unlockedBy(item); + } + + @Override + public T unlockedBy(TagKey tag) { + return super.unlockedBy(tag); + } + + @Override + protected T unlocks(String name, CriterionTriggerInstance trigger) { + this.advancement.addCriterion(name, trigger); + return (T) this; + } + + + @Override + protected void buildRecipe(Consumer cc) { + setupAdvancementForResult(); + cc.accept(new AbstractSingleInputRecipeBuilder.Result()); + } + + protected abstract RecipeSerializer getSerializer(); + + protected void serializeRecipeData(JsonObject root) { + root.add("input", primaryInput.toJson()); + + if (group != null && !group.isEmpty()) { + root.addProperty("group", group); + } + + root.add("result", ItemUtil.toJsonRecipe(output)); + } + + protected void setupAdvancementForResult() { + advancement.parent(RecipeBuilder.ROOT_RECIPE_ADVANCEMENT) + .addCriterion("has_the_recipe", RecipeUnlockedTrigger.unlocked(id)) + .rewards(net.minecraft.advancements.AdvancementRewards.Builder.recipe(id)) + .requirements(RequirementsStrategy.OR); + } + + protected ResourceLocation createAdvancementId() { + return id.withPrefix("recipes/" + category.getFolderName() + "/"); + } + + public class Result implements FinishedRecipe { + private final ResourceLocation advancementId; + + protected Result() { + this.advancementId = createAdvancementId(); + } + + @Override + public ResourceLocation getId() { + return AbstractSingleInputRecipeBuilder.this.getId(); + } + + @Override + public RecipeSerializer getType() { + return getSerializer(); + } + + @Nullable + @Override + public JsonObject serializeAdvancement() { + return advancement.serializeToJson(); + } + + @Nullable + @Override + public ResourceLocation getAdvancementId() { + return advancementId; + } + + @Override + public void serializeRecipeData(JsonObject root) { + AbstractSingleInputRecipeBuilder.this.serializeRecipeData(root); + } + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AbstractUnlockableRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/AbstractUnlockableRecipeBuilder.java new file mode 100644 index 00000000..73e069d3 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/AbstractUnlockableRecipeBuilder.java @@ -0,0 +1,35 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.ItemLike; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstractUnlockableRecipeBuilder extends AbstractSimpleRecipeBuilder { + + protected AbstractUnlockableRecipeBuilder(ResourceLocation id, ItemLike output) { + super(id, output); + } + + protected final Map unlocks = new HashMap<>(); + + @Override + public T unlockedBy(ItemLike item) { + return super.unlockedBy(item); + } + + @Override + public T unlockedBy(TagKey tag) { + return super.unlockedBy(tag); + } + + @Override + protected T unlocks(String name, CriterionTriggerInstance trigger) { + this.unlocks.put(name, trigger); + return (T) this; + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/AlloyingRecipe.java b/src/main/java/org/betterx/bclib/recipes/AlloyingRecipe.java index 015a5df0..eedbfbab 100644 --- a/src/main/java/org/betterx/bclib/recipes/AlloyingRecipe.java +++ b/src/main/java/org/betterx/bclib/recipes/AlloyingRecipe.java @@ -1,14 +1,13 @@ package org.betterx.bclib.recipes; import org.betterx.bclib.BCLib; -import org.betterx.bclib.config.PathConfig; import org.betterx.bclib.interfaces.AlloyingRecipeWorkstation; import org.betterx.bclib.interfaces.UnknownReceipBookCategory; import org.betterx.bclib.util.ItemUtil; -import org.betterx.bclib.util.RecipeHelper; import net.minecraft.core.NonNullList; import net.minecraft.core.RegistryAccess; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; @@ -130,77 +129,29 @@ public class AlloyingRecipe implements Recipe, UnknownReceipBookCateg return AlloyingRecipeWorkstation.getWorkstationIcon(); } - public static class Builder { - private final static Builder INSTANCE = new Builder(); - private static boolean exist; + public static class Builder extends AbstractDoubleInputRecipeBuilder { + private Builder(ResourceLocation id, ItemLike output) { + super(id, output); + this.experience = 0.0F; + this.smeltTime = 350; + } static Builder create(ResourceLocation id, ItemLike output) { - INSTANCE.id = id; - INSTANCE.group = String.format("%s_%s", GROUP, id); - INSTANCE.primaryInput = null; - INSTANCE.secondaryInput = null; - INSTANCE.output = output; - INSTANCE.experience = 0.0F; - INSTANCE.smeltTime = 350; - INSTANCE.count = 1; - INSTANCE.alright = RecipeHelper.exists(output); - exist = true; - - return INSTANCE; + return new Builder(id, output); } - private int count = 1; - private ResourceLocation id; - private Ingredient primaryInput; - private Ingredient secondaryInput; - private ItemLike output; - private String group; private float experience; private int smeltTime; - private boolean alright = true; - protected Builder() { - } - - public Builder checkConfig(PathConfig config) { - exist &= config.getBoolean("alloying", id.getPath(), true); - return this; - } - - public Builder setGroup(String group) { - this.group = group; - return this; - } + @Override public Builder setOutputCount(int count) { - this.count = count; - return this; + return super.setOutputCount(count); } - public Builder setPrimaryInput(ItemLike... inputs) { - for (ItemLike item : inputs) { - this.alright &= RecipeHelper.exists(item); - } - this.primaryInput = Ingredient.of(inputs); - return this; - } - - public Builder setSecondaryInput(ItemLike... inputs) { - for (ItemLike item : inputs) { - this.alright &= RecipeHelper.exists(item); - } - this.secondaryInput = Ingredient.of(inputs); - return this; - } - - public Builder setPrimaryInput(TagKey input) { - this.primaryInput = Ingredient.of(input); - return this; - } - - public Builder setSecondaryInput(TagKey input) { - this.secondaryInput = Ingredient.of(input); - return this; + @Override + public Builder setOutputTag(CompoundTag tag) { + return super.setOutputTag(tag); } public Builder setInput(ItemLike primaryInput, ItemLike secondaryInput) { @@ -225,46 +176,34 @@ public class AlloyingRecipe implements Recipe, UnknownReceipBookCateg return this; } - public void build() { - if (exist) { - if (primaryInput == null) { - BCLib.LOGGER.warning( - "Primary input for Alloying recipe can't be 'null', recipe {} will be ignored!", - id - ); - return; - } - if (secondaryInput == null) { - BCLib.LOGGER.warning( - "Secondary input for Alloying can't be 'null', recipe {} will be ignored!", - id - ); - return; - } - if (output == null) { - BCLib.LOGGER.warning("Output for Alloying can't be 'null', recipe {} will be ignored!", id); - return; - } - if (BCLRecipeManager.getRecipe(TYPE, id) != null) { - BCLib.LOGGER.warning("Can't add Alloying recipe! Id {} already exists!", id); - return; - } - if (!alright) { - BCLib.LOGGER.debug("Can't add Alloying recipe {}! Ingeredient or output not exists.", id); - return; - } - BCLRecipeManager.addRecipe( - TYPE, - new AlloyingRecipe( - id, - group, - primaryInput, - secondaryInput, - new ItemStack(output, count), - experience, - smeltTime - ) - ); + @Override + public Builder setGroup(String group) { + return super.setGroup(group); + } + + @Override + protected boolean checkRecipe() { + if (smeltTime < 0) { + BCLib.LOGGER.warning("Semelt-time for recipe {} most be positive!", id); + return false; + } + return super.checkRecipe(); + } + + @Override + protected RecipeSerializer getSerializer() { + return SERIALIZER; + } + + @Override + protected void serializeRecipeData(JsonObject root) { + super.serializeRecipeData(root); + + if (experience != 0) { + root.addProperty("experience", experience); + } + if (experience != 350) { + root.addProperty("smelttime", smeltTime); } } } @@ -275,9 +214,11 @@ public class AlloyingRecipe implements Recipe, UnknownReceipBookCateg JsonArray ingredients = GsonHelper.getAsJsonArray(json, "ingredients"); Ingredient primaryInput = Ingredient.fromJson(ingredients.get(0)); Ingredient secondaryInput = Ingredient.fromJson(ingredients.get(1)); + String group = GsonHelper.getAsString(json, "group", ""); + JsonObject result = GsonHelper.getAsJsonObject(json, "result"); - ItemStack output = ItemUtil.fromJsonRecipe(result); + ItemStack output = ItemUtil.fromJsonRecipeWithNBT(result); if (output == null) { throw new IllegalStateException("Output item does not exists!"); } diff --git a/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java b/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java index 2f925f65..dd7af79a 100644 --- a/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java +++ b/src/main/java/org/betterx/bclib/recipes/AnvilRecipe.java @@ -1,14 +1,11 @@ package org.betterx.bclib.recipes; import org.betterx.bclib.BCLib; -import org.betterx.bclib.config.PathConfig; import org.betterx.bclib.interfaces.UnknownReceipBookCategory; import org.betterx.bclib.util.ItemUtil; -import org.betterx.bclib.util.RecipeHelper; import org.betterx.worlds.together.tag.v3.CommonItemTags; import org.betterx.worlds.together.world.event.WorldBootstrap; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.client.Minecraft; import net.minecraft.core.Holder; import net.minecraft.core.NonNullList; @@ -16,7 +13,6 @@ import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; @@ -83,17 +79,7 @@ public class AnvilRecipe implements Recipe, UnknownReceipBookCategory } static Builder create(ResourceLocation id, ItemLike output) { - Builder.INSTANCE.id = id; - Builder.INSTANCE.input = null; - Builder.INSTANCE.output = output; - Builder.INSTANCE.inputCount = 1; - Builder.INSTANCE.toolLevel = 1; - Builder.INSTANCE.anvilLevel = 1; - Builder.INSTANCE.damage = 1; - Builder.INSTANCE.alright = true; - Builder.INSTANCE.exist = RecipeHelper.exists(output); - - return Builder.INSTANCE; + return new Builder(id, output); } @Override @@ -261,36 +247,54 @@ public class AnvilRecipe implements Recipe, UnknownReceipBookCategory return "AnvilRecipe [" + id + "]"; } - public static class Builder { - private final static Builder INSTANCE = new Builder(); + public static class Builder extends AbstractDoubleInputRecipeBuilder { + private int inputCount; + private int toolLevel; + private int anvilLevel; + private int damage; - private ResourceLocation id; - private Ingredient input; - private ItemLike output; - private int inputCount = 1; - private int outputCount = 1; - private int toolLevel = 1; - private int anvilLevel = 1; - private int damage = 1; - private boolean alright; - private boolean exist; + protected Builder(ResourceLocation id, ItemLike output) { + super(id, output); - private Builder() { + this.inputCount = 1; + this.toolLevel = 1; + this.anvilLevel = 1; + this.damage = 1; } + @Override + protected Builder setOutputTag(CompoundTag tag) { + return super.setOutputTag(tag); + } + + @Override + protected Builder setOutputCount(int count) { + return super.setOutputCount(count); + } + + /** + * @param inputItems + * @return + * @deprecated Use {@link #setPrimaryInput(ItemLike...)} instead + */ + @Deprecated(forRemoval = true) public Builder setInput(ItemLike... inputItems) { - this.alright &= RecipeHelper.exists(inputItems); - this.setInput(Ingredient.of(inputItems)); - return this; + return super.setPrimaryInput(inputItems); } + /** + * @param inputTag + * @return + * @deprecated Use {@link #setPrimaryInput(TagKey)} instead + */ + @Deprecated(forRemoval = true) public Builder setInput(TagKey inputTag) { - this.setInput(Ingredient.of(inputTag)); - return this; + return super.setPrimaryInput(inputTag); } + @Deprecated(forRemoval = true) public Builder setInput(Ingredient ingredient) { - this.input = ingredient; + this.primaryInput = ingredient; return this; } @@ -299,10 +303,6 @@ public class AnvilRecipe implements Recipe, UnknownReceipBookCategory return this; } - public Builder setOutputCount(int count) { - this.outputCount = count; - return this; - } public Builder setToolLevel(int level) { this.toolLevel = level; @@ -319,44 +319,39 @@ public class AnvilRecipe implements Recipe, UnknownReceipBookCategory return this; } - public Builder checkConfig(PathConfig config) { - exist &= config.getBoolean("anvil", id.getPath(), true); - return this; + @Override + protected RecipeSerializer getSerializer() { + return SERIALIZER; } - public void build() { - if (!exist) { - return; + @Override + protected boolean checkRecipe() { + if (inputCount <= 0) { + BCLib.LOGGER.warning( + "Number of input items for Recipe must be positive. Recipe {} will be ignored!", + id + ); + return false; } + return super.checkRecipe(); + } - if (input == null) { - BCLib.LOGGER.warning("Input for Anvil recipe can't be 'null', recipe {} will be ignored!", id); - return; + @Override + protected void serializeRecipeData(JsonObject root) { + super.serializeRecipeData(root); + + if (inputCount > 1) { + root.addProperty("inputCount", inputCount); } - if (output == null) { - BCLib.LOGGER.warning("Output for Anvil recipe can't be 'null', recipe {} will be ignored!", id); - return; + if (toolLevel != 1) { + root.addProperty("toolLevel", toolLevel); } - if (BCLRecipeManager.getRecipe(TYPE, id) != null) { - BCLib.LOGGER.warning("Can't add Anvil recipe! Id {} already exists!", id); - return; + if (anvilLevel != 1) { + root.addProperty("anvilLevel", anvilLevel); } - if (!alright) { - BCLib.LOGGER.debug("Can't add Anvil recipe {}! Ingeredient or output not exists.", id); - return; + if (damage != 1) { + root.addProperty("damage", damage); } - BCLRecipeManager.addRecipe( - TYPE, - new AnvilRecipe( - id, - input, - new ItemStack(output, outputCount), - inputCount, - toolLevel, - anvilLevel, - damage - ) - ); } } @@ -365,19 +360,11 @@ public class AnvilRecipe implements Recipe, UnknownReceipBookCategory public AnvilRecipe fromJson(ResourceLocation id, JsonObject json) { Ingredient input = Ingredient.fromJson(json.get("input")); JsonObject result = GsonHelper.getAsJsonObject(json, "result"); - ItemStack output = ItemUtil.fromJsonRecipe(result); + ItemStack output = ItemUtil.fromJsonRecipeWithNBT(result); if (output == null) { throw new IllegalStateException("Output item does not exists!"); } - if (result.has("nbt")) { - try { - String nbtData = GsonHelper.getAsString(result, "nbt"); - CompoundTag nbt = TagParser.parseTag(nbtData); - output.setTag(nbt); - } catch (CommandSyntaxException ex) { - BCLib.LOGGER.warning("Error parse nbt data for output.", ex); - } - } + int inputCount = GsonHelper.getAsInt(json, "inputCount", 1); int toolLevel = GsonHelper.getAsInt(json, "toolLevel", 1); int anvilLevel = GsonHelper.getAsInt(json, "anvilLevel", 1); diff --git a/src/main/java/org/betterx/bclib/recipes/BCLRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/BCLRecipeBuilder.java index 17529b73..11747219 100644 --- a/src/main/java/org/betterx/bclib/recipes/BCLRecipeBuilder.java +++ b/src/main/java/org/betterx/bclib/recipes/BCLRecipeBuilder.java @@ -12,23 +12,23 @@ public class BCLRecipeBuilder { return AnvilRecipe.create(id, output); } - public static BlastFurnaceRecipe blasting(ResourceLocation id, ItemLike output) { - return BlastFurnaceRecipe.make(id, output); + public static CookingRecipeBuilder blasting(ResourceLocation id, ItemLike output) { + return CookingRecipeBuilder.make(id, output).disableSmelter().enableBlastFurnace(); } - public static GridRecipe crafting(ResourceLocation id, ItemLike output) { - return GridRecipe.make(id, output); + public static CraftingRecipeBuilder crafting(ResourceLocation id, ItemLike output) { + return CraftingRecipeBuilder.make(id, output); } - public static FurnaceRecipe smelting(ResourceLocation id, ItemLike output) { - return FurnaceRecipe.make(id, output); + public static CookingRecipeBuilder smelting(ResourceLocation id, ItemLike output) { + return CookingRecipeBuilder.make(id, output); } - public static SmithingTableRecipe smithing(ResourceLocation id, ItemLike output) { - return SmithingTableRecipe.make(id, output); + public static SmithingRecipeBuilder smithing(ResourceLocation id, ItemLike output) { + return SmithingRecipeBuilder.make(id, output); } - public static StoneCutterRecipe stonecutting(ResourceLocation id, ItemLike output) { - return StoneCutterRecipe.make(id, output); + public static StonecutterRecipeBuilder stonecutting(ResourceLocation id, ItemLike output) { + return StonecutterRecipeBuilder.make(id, output); } } diff --git a/src/main/java/org/betterx/bclib/recipes/BlastFurnaceRecipe.java b/src/main/java/org/betterx/bclib/recipes/BlastFurnaceRecipe.java deleted file mode 100644 index f9b5a742..00000000 --- a/src/main/java/org/betterx/bclib/recipes/BlastFurnaceRecipe.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.betterx.bclib.recipes; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.BlastingRecipe; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.ItemLike; - -public class BlastFurnaceRecipe extends CookingRecipe { - BlastFurnaceRecipe(ResourceLocation id, ItemLike output) { - super(id, RecipeType.BLASTING, output); - } - - static BlastFurnaceRecipe make(String modID, String name, ItemLike output) { - return make(new ResourceLocation(modID, name), output); - } - - static BlastFurnaceRecipe make(ResourceLocation id, ItemLike output) { - BlastFurnaceRecipe res = new BlastFurnaceRecipe(id, output); - res.createAdvancement(id, false); - return res; - } - - @Override - protected BlastingRecipe buildRecipe() { - return new BlastingRecipe( - id, - group, - bookCategory, - input, - new ItemStack(output, count), - experience, - cookingTime - ); - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/CookingRecipe.java b/src/main/java/org/betterx/bclib/recipes/CookingRecipe.java deleted file mode 100644 index 14e0c750..00000000 --- a/src/main/java/org/betterx/bclib/recipes/CookingRecipe.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.betterx.bclib.recipes; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.crafting.CookingBookCategory; -import net.minecraft.world.item.crafting.Recipe; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.ItemLike; - -public abstract class CookingRecipe> extends AbstractSimpleRecipe { - protected float experience; - protected int cookingTime; - protected CookingBookCategory bookCategory; - - CookingRecipe(ResourceLocation id, RecipeType type, ItemLike output) { - this(id, type, type.toString(), output); - } - - CookingRecipe(ResourceLocation id, RecipeType type, String category, ItemLike output) { - super(id, type, category, output); - cookingTime = 1000; - experience = 0; - this.bookCategory = CookingBookCategory.MISC; - } - - public T setInput(ItemLike in) { - return super.setInput(in); - } - - public T setInput(TagKey in) { - return super.setInput(in); - } - - public T setExperience(float xp) { - experience = xp; - return (T) this; - } - - public T setCookingTime(int time) { - cookingTime = time; - return (T) this; - } - - public T setCookingBookCategory(CookingBookCategory c) { - bookCategory = c; - return (T) this; - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/CookingRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/CookingRecipeBuilder.java new file mode 100644 index 00000000..8ef7c97e --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/CookingRecipeBuilder.java @@ -0,0 +1,208 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.SimpleCookingRecipeBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.ItemLike; + +import java.util.function.Consumer; + +public class CookingRecipeBuilder extends AbstractUnlockableRecipeBuilder { + + protected float xp; + protected int cookingTime; + + boolean blasting, campfire, smoker, smelting; + + static CookingRecipeBuilder make(ResourceLocation id, ItemLike output) { + return new CookingRecipeBuilder(id, output); + } + + protected CookingRecipeBuilder(ResourceLocation id, ItemLike output) { + super(id, output); + this.xp = 0; + this.cookingTime = 200; + this.smelting = true; + } + + + /** + * Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + * + * @deprecated Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + */ + @Deprecated(forRemoval = true) + public CookingRecipeBuilder setInput(ItemLike in) { + return this.setPrimaryInputAndUnlock(in); + } + + /** + * Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + * + * @param in + * @return + * @deprecated Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + */ + @Deprecated(forRemoval = true) + public CookingRecipeBuilder setInput(TagKey in) { + return this.setPrimaryInputAndUnlock(in); + } + + public CookingRecipeBuilder setExperience(float xp) { + this.xp = xp; + return this; + } + + public CookingRecipeBuilder setCookingTime(int time) { + this.cookingTime = time; + return this; + } + + @Override + protected boolean checkRecipe() { + if (smelting == false && blasting == false && campfire == false && smoker == false) { + BCLib.LOGGER.warning( + "No target (smelting, blasting, campfire or somer) for cooking recipe was selected. Recipe {} will be ignored!", + id + ); + return false; + } + + if (cookingTime < 0) { + BCLib.LOGGER.warning( + "cooking time must be positive. Recipe {} will be ignored!", + id + ); + return false; + } + return super.checkRecipe(); + } + + public CookingRecipeBuilder enableSmelter() { + this.smelting = true; + return this; + } + + public CookingRecipeBuilder disableSmelter() { + this.smelting = false; + return this; + } + + public CookingRecipeBuilder enableBlastFurnace() { + this.blasting = true; + return this; + } + + public CookingRecipeBuilder disableBlastFurnace() { + this.blasting = false; + return this; + } + + public CookingRecipeBuilder enableCampfire() { + this.campfire = true; + return this; + } + + public CookingRecipeBuilder disableCampfire() { + this.campfire = false; + return this; + } + + public CookingRecipeBuilder enableSmoker() { + this.smoker = true; + return this; + } + + public CookingRecipeBuilder disableSmoker() { + this.smoker = false; + return this; + } + + public void build(boolean blasting, boolean campfire, boolean smoker) { + this.enableSmelter(); + this.blasting = blasting; + this.campfire = campfire; + this.smoker = smoker; + + build(); + } + + public void buildWithBlasting() { + build(true, false, false); + } + + public void buildFoodlike() { + build(false, true, true); + } + + private void buildRecipe(Consumer cc, SimpleCookingRecipeBuilder builder, String postfix) { + ResourceLocation loc = new ResourceLocation(id.getNamespace(), id.getPath() + "_" + postfix); + for (var item : unlocks.entrySet()) { + builder.unlockedBy(item.getKey(), item.getValue()); + } + builder.save(cc, loc); + } + + @Override + protected void buildRecipe(Consumer cc) { + if (smelting) { + buildRecipe( + cc, + SimpleCookingRecipeBuilder.smelting( + primaryInput, + category, + output.getItem(), + xp, + cookingTime + ), + "smelting" + ); + } + + if (blasting) { + buildRecipe( + cc, + SimpleCookingRecipeBuilder.blasting( + primaryInput, + category, + output.getItem(), + xp, + cookingTime / 2 + ), + "blasting" + ); + } + + if (campfire) { + buildRecipe( + cc, + SimpleCookingRecipeBuilder.campfireCooking( + primaryInput, + category, + output.getItem(), + xp, + cookingTime * 3 + ), + "campfire" + ); + } + + if (smoker) { + buildRecipe( + cc, + SimpleCookingRecipeBuilder.campfireCooking( + primaryInput, + category, + output.getItem(), + xp, + cookingTime / 2 + ), + "smoker" + ); + } + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/CraftingRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/CraftingRecipeBuilder.java new file mode 100644 index 00000000..2696d442 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/CraftingRecipeBuilder.java @@ -0,0 +1,225 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; +import org.betterx.bclib.config.PathConfig; + +import net.minecraft.advancements.CriterionTriggerInstance; +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.ShapedRecipeBuilder; +import net.minecraft.data.recipes.ShapelessRecipeBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class CraftingRecipeBuilder extends AbstractBaseRecipeBuilder { + private String[] shape; + private boolean showNotification; + + protected CraftingRecipeBuilder( + ResourceLocation id, + ItemLike output + ) { + super(id, output); + this.showNotification = true; + this.shape = new String[]{"#"}; + } + + static CraftingRecipeBuilder make(ResourceLocation id, ItemLike output) { + return new CraftingRecipeBuilder(id, output); + } + + protected final Map unlocks = new HashMap<>(); + protected final Map materials = new HashMap<>(); + + @Override + public CraftingRecipeBuilder setOutputCount(int count) { + return super.setOutputCount(count); + } + + public CraftingRecipeBuilder addMaterial(char key, TagKey value) { + unlockedBy(value); + materials.put(key, Ingredient.of(value)); + return this; + } + + public CraftingRecipeBuilder addMaterial(char key, ItemStack... values) { + unlockedBy(values); + return addMaterial(key, Ingredient.of(Arrays.stream(values))); + } + + public CraftingRecipeBuilder addMaterial(char key, ItemLike... values) { + for (ItemLike item : values) { + this.alright &= BCLRecipeManager.exists(item); + } + unlockedBy(values); + return addMaterial(key, Ingredient.of(values)); + } + + private CraftingRecipeBuilder addMaterial(char key, Ingredient value) { + materials.put(key, value); + return this; + } + + public CraftingRecipeBuilder setShape(String... shape) { + this.shape = shape; + return this; + } + + public CraftingRecipeBuilder shapeless() { + this.shape = null; + return this; + } + + /** + * @param shape + * @return + * @deprecated Use {@link #shapeless()} instead + */ + @Deprecated(forRemoval = true) + public CraftingRecipeBuilder setList(String shape) { + return shapeless(); + } + + public CraftingRecipeBuilder showNotification(boolean showNotification) { + this.showNotification = showNotification; + return this; + } + + + @Override + public CraftingRecipeBuilder unlockedBy(ItemLike item) { + return super.unlockedBy(item); + } + + @Override + public CraftingRecipeBuilder unlockedBy(TagKey tag) { + return super.unlockedBy(tag); + } + + @Override + public CraftingRecipeBuilder unlockedBy(ItemLike... items) { + return super.unlockedBy(items); + } + + @Override + public CraftingRecipeBuilder unlockedBy(ItemStack... stacks) { + return super.unlockedBy(stacks); + } + + @Override + protected CraftingRecipeBuilder unlocks(String name, CriterionTriggerInstance trigger) { + this.unlocks.put(name, trigger); + return this; + } + + @Override + public CraftingRecipeBuilder setGroup(String group) { + return super.setGroup(group); + } + + + @Override + protected boolean checkRecipe() { + if (shape != null) return checkShaped(); + else return checkShapeless(); + } + + @Override + protected void buildRecipe(Consumer cc) { + if (shape != null) buildShaped(cc); + else buildShapeless(cc); + } + + protected boolean checkShapeless() { + if (materials.size() == 0) { + BCLib.LOGGER.warning("Recipe {} does not contain a material!", id); + return false; + } + return super.checkRecipe(); + } + + protected void buildShapeless(Consumer cc) { + final ShapelessRecipeBuilder builder = ShapelessRecipeBuilder.shapeless( + category, output.getItem(), output.getCount() + ); + for (Map.Entry item : unlocks.entrySet()) { + builder.unlockedBy(item.getKey(), item.getValue()); + } + for (Map.Entry mat : materials.entrySet()) { + builder.requires(mat.getValue()); + } + + builder.group(group); + builder.save(cc, id); + } + + protected boolean checkShaped() { + if (shape == null || shape.length == 0) { + BCLib.LOGGER.warning("Recipe {} does not contain a shape!", id); + return false; + } + if (shape.length > 3) { + BCLib.LOGGER.warning("Recipe {} shape contains more than three lines!", id); + return false; + } + int width = shape[0].length(); + if (width > 3) { + BCLib.LOGGER.warning("Recipe {} shape is wider than three!", id); + return false; + } + String allLines = ""; + for (int i = 0; i < shape.length; i++) { + if (shape[i].length() != width) { + BCLib.LOGGER.warning("All lines in the shape of Recipe {} should be the same length!", id); + return false; + } + allLines += shape[i]; + } + allLines = allLines.replaceAll(" ", ""); + if (allLines.length() == 1) { + BCLib.LOGGER.warning("Recipe {} only takes in a single item and should be shapeless", id); + return false; + } + + for (int i = 0; i < allLines.length(); i++) { + char c = allLines.charAt(i); + if (!materials.containsKey(c)) { + BCLib.LOGGER.warning("Recipe {} is missing the material definition for '" + c + "'!", id); + return false; + } + } + + return super.checkRecipe(); + } + + + protected void buildShaped(Consumer cc) { + final ShapedRecipeBuilder builder = ShapedRecipeBuilder.shaped( + category, output.getItem(), output.getCount() + ); + for (Map.Entry item : unlocks.entrySet()) { + builder.unlockedBy(item.getKey(), item.getValue()); + } + for (Map.Entry mat : materials.entrySet()) { + builder.define(mat.getKey(), mat.getValue()); + } + for (String row : shape) { + builder.pattern(row); + } + builder.group(group); + + builder.save(cc, id); + } + + public CraftingRecipeBuilder checkConfig(PathConfig config) { + return this; + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java b/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java deleted file mode 100644 index 873c1b77..00000000 --- a/src/main/java/org/betterx/bclib/recipes/FurnaceRecipe.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.betterx.bclib.recipes; - -import org.betterx.bclib.config.PathConfig; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.*; -import net.minecraft.world.level.ItemLike; - -public class FurnaceRecipe extends AbstractAdvancementRecipe { - private static final FurnaceRecipe INSTANCE = new FurnaceRecipe(); - - private ResourceLocation id; - private Ingredient input; - private ItemLike output; - private boolean exist; - private String group; - private int count; - private int time; - private float xp; - protected CookingBookCategory bookCategory; - - private FurnaceRecipe() { - } - - static FurnaceRecipe make(ResourceLocation id, ItemLike output) { - INSTANCE.id = id; - INSTANCE.group = ""; - INSTANCE.input = null; - INSTANCE.output = output; - INSTANCE.count = 1; - INSTANCE.time = 200; - INSTANCE.xp = 0; - INSTANCE.exist = BCLRecipeManager.exists(output); - INSTANCE.createAdvancement(INSTANCE.id, false); - INSTANCE.bookCategory = CookingBookCategory.MISC; - - return INSTANCE; - } - - public FurnaceRecipe setInput(ItemLike input) { - exist &= BCLRecipeManager.exists(input); - this.input = Ingredient.of(input); - unlockedBy(input); - return this; - } - - public FurnaceRecipe setInput(TagKey tag) { - this.input = Ingredient.of(tag); - unlockedBy(tag); - return this; - } - - public FurnaceRecipe checkConfig(PathConfig config) { - exist &= config.getBoolean("furnace", id.getPath(), true); - return this; - } - - public FurnaceRecipe setGroup(String group) { - this.group = group; - return this; - } - - public FurnaceRecipe setOutputCount(int count) { - this.count = count; - return this; - } - - public FurnaceRecipe setExperience(float xp) { - this.xp = xp; - return this; - } - - public FurnaceRecipe setCookingTime(int time) { - this.time = time; - return this; - } - - public FurnaceRecipe setCookingBookCategory(CookingBookCategory c) { - bookCategory = c; - return this; - } - - public void build() { - build(false, false, false); - } - - public void buildWithBlasting() { - build(true, false, false); - } - - public void buildFoodlike() { - build(false, true, true); - } - - public void build(boolean blasting, boolean campfire, boolean smoker) { - if (!exist) { - return; - } - - SmeltingRecipe recipe = new SmeltingRecipe( - new ResourceLocation(id + "_smelting"), - group, - bookCategory, - input, - new ItemStack(output, count), - xp, - time - ); - BCLRecipeManager.addRecipe(RecipeType.SMELTING, recipe); - registerAdvancement(recipe, output); - - if (blasting) { - BlastingRecipe recipe2 = new BlastingRecipe( - new ResourceLocation(id + "_blasting"), - group, - bookCategory, - input, - new ItemStack(output, count), - xp, - time / 2 - ); - - BCLRecipeManager.addRecipe(RecipeType.BLASTING, recipe2); - } - - if (campfire) { - CampfireCookingRecipe recipe2 = new CampfireCookingRecipe( - new ResourceLocation(id + "_campfire"), - group, - bookCategory, - input, - new ItemStack(output, count), - xp, - time * 3 - ); - - BCLRecipeManager.addRecipe(RecipeType.CAMPFIRE_COOKING, recipe2); - } - - if (smoker) { - SmokingRecipe recipe2 = new SmokingRecipe( - new ResourceLocation(id + "_smoker"), - group, - bookCategory, - input, - new ItemStack(output, count), - xp, - time / 2 - ); - - BCLRecipeManager.addRecipe(RecipeType.SMOKING, recipe2); - } - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/GridRecipe.java b/src/main/java/org/betterx/bclib/recipes/GridRecipe.java deleted file mode 100644 index edee418b..00000000 --- a/src/main/java/org/betterx/bclib/recipes/GridRecipe.java +++ /dev/null @@ -1,249 +0,0 @@ -package org.betterx.bclib.recipes; - -import org.betterx.bclib.BCLib; -import org.betterx.bclib.config.PathConfig; - -import net.minecraft.core.NonNullList; -import net.minecraft.data.recipes.*; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.crafting.CraftingRecipe; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.ItemLike; - -import com.google.common.collect.Maps; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -public class GridRecipe extends AbstractAdvancementRecipe { - private static final GridRecipe INSTANCE = new GridRecipe(); - - private ResourceLocation id; - private ItemLike output; - - private String group; - private RecipeType type; - private boolean shaped; - private String[] shape; - private final Map materialKeys = Maps.newHashMap(); - private final Map> materialTagKeys = Maps.newHashMap(); - private int count; - private boolean showNotification; - private boolean exist; - - protected RecipeCategory bookCategory; - - private GridRecipe() { - } - - /** - * Please use {@link BCLRecipeBuilder#crafting(ResourceLocation, ItemLike)} instead - * - * @param id - * @param output - * @return - */ - private GridRecipe(ResourceLocation id, ItemLike output) { - this.id = id; - this.output = output; - - this.group = ""; - this.type = RecipeType.CRAFTING; - this.shaped = true; - this.showNotification = true; - this.shape = new String[]{"#"}; - this.materialKeys.clear(); - this.count = 1; - this.bookCategory = RecipeCategory.MISC; - - this.exist = output != null && BCLRecipeManager.exists(output); - this.createAdvancement(id, output); - } - - static GridRecipe make(ResourceLocation id, ItemLike output) { - return new GridRecipe(id, output); - } - - public GridRecipe checkConfig(PathConfig config) { - exist &= config.getBoolean("grid", id.getPath(), true); - return this; - } - - public GridRecipe setGroup(String group) { - this.group = group; - return this; - } - - public GridRecipe setShape(String... shape) { - this.shape = shape; - return this; - } - - public GridRecipe setList(String shape) { - this.shape = new String[]{shape}; - this.shaped = false; - return this; - } - - public GridRecipe addMaterial(char key, TagKey value) { - unlockedBy(value); - materialTagKeys.put(key, value); - return this; - } - - public GridRecipe addMaterial(char key, ItemStack... values) { - unlockedBy(values); - return addMaterial(key, Ingredient.of(Arrays.stream(values))); - } - - public GridRecipe addMaterial(char key, ItemLike... values) { - for (ItemLike item : values) { - exist &= BCLRecipeManager.exists(item); - } - unlockedBy(values); - return addMaterial(key, Ingredient.of(values)); - } - - private GridRecipe addMaterial(char key, Ingredient value) { - materialKeys.put(key, value); - return this; - } - - public GridRecipe setOutputCount(int count) { - this.count = count; - return this; - } - - public GridRecipe showNotification(boolean showNotification) { - this.showNotification = showNotification; - return this; - } - - private NonNullList getMaterials(int width, int height) { - NonNullList materials = NonNullList.withSize(width * height, Ingredient.EMPTY); - int pos = 0; - boolean hasNonEmpty = false; - for (String line : shape) { - for (int i = 0; i < width; i++) { - char c = line.charAt(i); - Ingredient material = materialKeys.containsKey(c) - ? materialKeys.get(c) - : Ingredient.of(materialTagKeys.get(c)); - if (material != null && !material.isEmpty()) hasNonEmpty = true; - materials.set(pos++, material == null ? Ingredient.EMPTY : material); - } - } - if (!hasNonEmpty) return null; - return materials; - } - - public GridRecipe setCategory(RecipeCategory c) { - bookCategory = c; - return this; - } - - private static List RECIPES; - - public GridRecipe build() { - if (RECIPES == null) RECIPES = new ArrayList<>(); - RECIPES.add(this); - return this; - } - - public static void registerRecipes(Consumer cc) { - if (RECIPES == null) return; - - for (var r : RECIPES) { - r.build(cc); - } - RECIPES.clear(); - } - - public void build(Consumer cc) { - if (!exist) { - BCLib.LOGGER.warning("Unable to build Recipe " + id); - return; - } - - int height = shape.length; - int width = shape[0].length(); - ItemStack result = new ItemStack(output, count); - if (result.is(Items.AIR)) { - BCLib.LOGGER.warning("Unable to build Recipe " + id + ": Result is AIR"); - return; - } - - NonNullList materials = this.getMaterials(width, height); - if (materials == null || materials.isEmpty()) { - BCLib.LOGGER.warning("Unable to build Recipe " + id + ": Empty Material List"); - return; - } - - if (shaped) { - final ShapedRecipeBuilder builder = ShapedRecipeBuilder - .shaped(bookCategory, output, count) - .group(group) - .showNotification(showNotification); - - for (String row : this.shape) { - builder.pattern(row); - } - - for (Map.Entry in : materialKeys.entrySet()) { - Arrays - .stream(in.getValue().getItems()) - .filter(i -> i.getCount() > 0) - .forEach(stack -> builder.unlockedBy( - "has_" + stack.getDescriptionId(), - RecipeProvider.has(stack.getItem()) - )); - - builder.define(in.getKey(), in.getValue()); - } - - for (Map.Entry> in : materialTagKeys.entrySet()) { - builder.unlockedBy( - "has_tag_" + in.getValue().location().getNamespace() + "_" + in.getValue().location().getPath(), - RecipeProvider.has(in.getValue()) - ); - - builder.define(in.getKey(), in.getValue()); - } - builder.save(cc, id); - } else { - final ShapelessRecipeBuilder builder = ShapelessRecipeBuilder - .shapeless(bookCategory, output, count) - .group(group); - - for (Map.Entry in : materialKeys.entrySet()) { - Arrays - .stream(in.getValue().getItems()) - .filter(i -> i.getCount() > 0) - .forEach(stack -> builder.unlockedBy( - "has_" + stack.getDescriptionId(), - RecipeProvider.has(stack.getItem()) - )); - - builder.requires(in.getValue()); - } - - for (Map.Entry> in : materialTagKeys.entrySet()) { - builder.unlockedBy( - "has_tag_" + in.getValue().location().getNamespace() + "_" + in.getValue().location().getPath(), - RecipeProvider.has(in.getValue()) - ); - - builder.requires(in.getValue()); - } - builder.save(cc, id); - } - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/SmithingRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/SmithingRecipeBuilder.java new file mode 100644 index 00000000..4c464fd2 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/SmithingRecipeBuilder.java @@ -0,0 +1,77 @@ +package org.betterx.bclib.recipes; + +import org.betterx.bclib.BCLib; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.SmithingTransformRecipeBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; + +import java.util.function.Consumer; + +public class SmithingRecipeBuilder extends AbstractUnlockableRecipeBuilder { + protected Ingredient addon; + + protected SmithingRecipeBuilder( + ResourceLocation id, + ItemLike output + ) { + super(id, output); + } + + static SmithingRecipeBuilder make(ResourceLocation id, ItemLike output) { + return new SmithingRecipeBuilder(id, output); + } + + /** + * @param in + * @return + * @deprecated Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + */ + @Deprecated(forRemoval = true) + public SmithingRecipeBuilder setBase(ItemLike in) { + return super.setPrimaryInputAndUnlock(in); + } + + /** + * @param in + * @return + * @deprecated use {@link #setPrimaryInputAndUnlock(TagKey)} instead + */ + @Deprecated(forRemoval = true) + public SmithingRecipeBuilder setBase(TagKey in) { + return super.setPrimaryInputAndUnlock(in); + } + + public SmithingRecipeBuilder setAddition(ItemLike item) { + this.addon = Ingredient.of(item); + return this; + } + + + @Override + protected boolean checkRecipe() { + if (addon == null || addon.isEmpty()) { + BCLib.LOGGER.warning( + "Addon for Recipe can't be 'null', recipe {} will be ignored!", + id + ); + return false; + } + return super.checkRecipe(); + } + + @Override + protected void buildRecipe(Consumer cc) { + final SmithingTransformRecipeBuilder builder = SmithingTransformRecipeBuilder.smithing( + Ingredient.EMPTY, primaryInput, addon, category, output.getItem() + ); + for (var item : unlocks.entrySet()) { + builder.unlocks(item.getKey(), item.getValue()); + } + builder.save(cc, id); + } +} diff --git a/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java b/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java deleted file mode 100644 index 1178f420..00000000 --- a/src/main/java/org/betterx/bclib/recipes/SmithingTableRecipe.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.betterx.bclib.recipes; - -import org.betterx.bclib.BCLib; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.SmithingRecipe; -import net.minecraft.world.item.crafting.SmithingTransformRecipe; -import net.minecraft.world.level.ItemLike; - -public class SmithingTableRecipe extends AbstractSimpleRecipe { - protected Ingredient addon; - - protected SmithingTableRecipe(ResourceLocation id, ItemLike output) { - super(id, RecipeType.SMITHING, output); - } - - - static SmithingTableRecipe make(ResourceLocation id, ItemLike output) { - SmithingTableRecipe res = new SmithingTableRecipe(id, output); - res.createAdvancement(id, false); - return res; - } - - public SmithingTableRecipe setBase(ItemLike in) { - return super.setInput(in); - } - - public SmithingTableRecipe setBase(TagKey in) { - return super.setInput(in); - } - - public SmithingTableRecipe setAddition(ItemLike in) { - this.exist &= BCLRecipeManager.exists(in); - this.addon = Ingredient.of(in); - unlockedBy(in); - return this; - } - - public SmithingTableRecipe setAddition(TagKey in) { - this.addon = Ingredient.of(in); - unlockedBy(in); - return this; - } - - @Override - protected boolean hasErrors() { - if (addon == null || addon.isEmpty()) { - BCLib.LOGGER.warning("Unable to build Recipe " + id + ": No Addon Ingredient"); - return true; - } - return super.hasErrors(); - } - - @Override - protected SmithingRecipe buildRecipe() { - return new SmithingTransformRecipe(id, input, addon, null, new ItemStack(output, count)); - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/StoneCutterRecipe.java b/src/main/java/org/betterx/bclib/recipes/StoneCutterRecipe.java deleted file mode 100644 index de64ab9e..00000000 --- a/src/main/java/org/betterx/bclib/recipes/StoneCutterRecipe.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.betterx.bclib.recipes; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.world.Container; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.item.crafting.StonecutterRecipe; -import net.minecraft.world.level.ItemLike; - -public class StoneCutterRecipe extends AbstractSimpleRecipe { - StoneCutterRecipe(ResourceLocation id, ItemLike output) { - super(id, RecipeType.STONECUTTING, "stonecutting", output); - } - - static StoneCutterRecipe make(ResourceLocation id, ItemLike output) { - StoneCutterRecipe res = new StoneCutterRecipe(id, output); - res.createAdvancement(id, false); - return res; - } - - public StoneCutterRecipe setInput(ItemLike in) { - return super.setInput(in); - } - - public StoneCutterRecipe setInput(TagKey in) { - return super.setInput(in); - } - - @Override - protected StonecutterRecipe buildRecipe() { - return new StonecutterRecipe(id, group, input, new ItemStack(output, count)); - } -} diff --git a/src/main/java/org/betterx/bclib/recipes/StonecutterRecipeBuilder.java b/src/main/java/org/betterx/bclib/recipes/StonecutterRecipeBuilder.java new file mode 100644 index 00000000..0fc23d71 --- /dev/null +++ b/src/main/java/org/betterx/bclib/recipes/StonecutterRecipeBuilder.java @@ -0,0 +1,67 @@ +package org.betterx.bclib.recipes; + +import net.minecraft.data.recipes.FinishedRecipe; +import net.minecraft.data.recipes.SingleItemRecipeBuilder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.ItemLike; + +import java.util.function.Consumer; + +public class StonecutterRecipeBuilder extends AbstractUnlockableRecipeBuilder { + + protected StonecutterRecipeBuilder( + ResourceLocation id, + ItemLike output + ) { + super(id, output); + } + + static StonecutterRecipeBuilder make(ResourceLocation id, ItemLike output) { + return new StonecutterRecipeBuilder(id, output); + } + + @Override + public StonecutterRecipeBuilder setGroup(String group) { + return super.setGroup(group); + } + + /** + * @param in + * @return + * @deprecated Use {@link #setPrimaryInputAndUnlock(ItemLike...)} instead + */ + @Deprecated(forRemoval = true) + public StonecutterRecipeBuilder setInput(ItemLike in) { + return super.setPrimaryInputAndUnlock(in); + } + + /** + * @param in + * @return + * @deprecated use {@link #setPrimaryInputAndUnlock(TagKey)} instead + */ + @Deprecated(forRemoval = true) + public StonecutterRecipeBuilder setInput(TagKey in) { + return super.setPrimaryInputAndUnlock(in); + } + + @Override + public StonecutterRecipeBuilder setOutputCount(int count) { + return super.setOutputCount(count); + } + + @Override + protected void buildRecipe(Consumer cc) { + final SingleItemRecipeBuilder builder = SingleItemRecipeBuilder.stonecutting( + primaryInput, category, output.getItem(), output.getCount() + ); + for (var item : unlocks.entrySet()) { + builder.unlockedBy(item.getKey(), item.getValue()); + } + builder.group(group); + builder.save(cc, id); + } + +} diff --git a/src/main/java/org/betterx/bclib/util/ItemUtil.java b/src/main/java/org/betterx/bclib/util/ItemUtil.java index f3fc9848..c5a2972c 100644 --- a/src/main/java/org/betterx/bclib/util/ItemUtil.java +++ b/src/main/java/org/betterx/bclib/util/ItemUtil.java @@ -2,11 +2,16 @@ package org.betterx.bclib.util; import org.betterx.bclib.BCLib; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.TagParser; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.GsonHelper; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; import com.google.gson.JsonObject; @@ -54,6 +59,20 @@ public class ItemUtil { return null; } + public static ItemStack fromJsonRecipeWithNBT(JsonObject recipe) { + ItemStack output = ItemUtil.fromJsonRecipe(recipe); + if (output != null && recipe.has("nbt")) { + try { + String nbtData = GsonHelper.getAsString(recipe, "nbt"); + CompoundTag nbt = TagParser.parseTag(nbtData); + output.setTag(nbt); + } catch (CommandSyntaxException ex) { + BCLib.LOGGER.warning("Error parse nbt data for output.", ex); + } + } + return output; + } + @Nullable public static ItemStack fromJsonRecipe(JsonObject recipe) { try { @@ -71,4 +90,42 @@ public class ItemUtil { } return null; } + + + public static JsonObject toJsonRecipeWithNBT(ItemStack stack) { + return toJsonRecipeWithNBT(stack.getItem(), stack.getCount(), stack.getTag()); + } + + public static JsonObject toJsonRecipeWithNBT(ItemLike item, int count, CompoundTag nbt) { + JsonObject root = toJsonRecipe(item, count); + if (nbt != null) { + final String nbtData = NbtUtils.prettyPrint(nbt); + root.addProperty("nbt", nbtData); + //TODO: just for testing + try { + TagParser.parseTag(nbtData); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + } + return root; + } + + public static JsonObject toJsonRecipe(ItemStack stack) { + return toJsonRecipe(stack.getItem(), stack.getCount()); + } + + public static JsonObject toJsonRecipe(ItemLike item, int count) { + final ResourceLocation id = BuiltInRegistries.ITEM.getKey(item.asItem()); + if (id == null) { + throw new IllegalStateException("Unknown Item " + item); + } + + final JsonObject root = new JsonObject(); + root.addProperty("item", BuiltInRegistries.ITEM.getKey(item.asItem()).toString()); + if (count > 1) { + root.addProperty("count", count); + } + return root; + } } diff --git a/src/main/java/org/betterx/bclib/util/JsonFactory.java b/src/main/java/org/betterx/bclib/util/JsonFactory.java index 5c7304da..3048cec0 100644 --- a/src/main/java/org/betterx/bclib/util/JsonFactory.java +++ b/src/main/java/org/betterx/bclib/util/JsonFactory.java @@ -129,4 +129,5 @@ public class JsonFactory { JsonElement elem = object.get(member); return elem == null ? def : elem.getAsString(); } + } diff --git a/src/main/java/org/betterx/datagen/bclib/tests/TestRecipes.java b/src/main/java/org/betterx/datagen/bclib/tests/TestRecipes.java index 3a95f5bd..dbff05dd 100644 --- a/src/main/java/org/betterx/datagen/bclib/tests/TestRecipes.java +++ b/src/main/java/org/betterx/datagen/bclib/tests/TestRecipes.java @@ -1,24 +1,24 @@ package org.betterx.datagen.bclib.tests; import org.betterx.bclib.BCLib; +import org.betterx.bclib.api.v3.datagen.RecipeDataProvider; import org.betterx.bclib.recipes.BCLRecipeBuilder; -import org.betterx.bclib.recipes.GridRecipe; +import org.betterx.bclib.recipes.CraftingRecipeBuilder; +import org.betterx.worlds.together.WorldsTogether; -import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.world.item.Items; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; -import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; -import java.util.function.Consumer; +import java.util.List; -public class TestRecipes extends FabricRecipeProvider { +public class TestRecipes extends RecipeDataProvider { public TestRecipes(FabricDataOutput output) { - super(output); + super(List.of(BCLib.MOD_ID, WorldsTogether.MOD_ID), output); } - final GridRecipe WONDER = BCLRecipeBuilder + final CraftingRecipeBuilder WONDER = BCLRecipeBuilder .crafting(BCLib.makeID("test_star"), Items.NETHER_STAR) .setOutputCount(1) .setShape("ggg", "glg", "ggg") @@ -27,8 +27,4 @@ public class TestRecipes extends FabricRecipeProvider { .setCategory(RecipeCategory.TOOLS) .build(); - @Override - public void buildRecipes(Consumer exporter) { - GridRecipe.registerRecipes(exporter); - } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index ec989ca5..a12505c1 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "bclib", - "version": "2.2.5", + "version": "2.3.0", "name": "BCLib", "description": "A library for BetterX team mods", "authors": [