[Change] Recipe Handling moved to Datagen

This commit is contained in:
Frank 2023-04-12 16:59:55 +02:00
parent f60fcd02d5
commit 809bfddd32
28 changed files with 1279 additions and 1121 deletions

View file

@ -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<FinishedRecipe> cc);
}

View file

@ -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<DatapackRecipeBuilder> RECIPES;
@Nullable
protected final List<String> modIDs;
public RecipeDataProvider(@Nullable List<String> modIDs, FabricDataOutput output) {
super(output);
this.modIDs = modIDs;
}
@Override
public void buildRecipes(Consumer<FinishedRecipe> 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);
}
}

View file

@ -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();

View file

@ -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<I extends Item> {
}
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) {

View file

@ -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.
* <p>
* 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.
* <p>
* 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}
* <p>
* 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}
* <p>
* For Example {@link GridRecipe} will call this in the {@link GridRecipe#make(ResourceLocation, ItemLike)}-Method.
* <p>
* 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.
* <p>
* If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance
* using {@link #getAdvancementBuilder()}
* <p>
* 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.
* <p>
* If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance
* using {@link #getAdvancementBuilder()}
* <p>
* 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.
* <p>
* If you need to use other unlock-Criteria, you can get the {@link AdvancementManager.Builder}-Instance
* using {@link #getAdvancementBuilder()}
* <p>
* 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<Item> 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.
* <p>
* 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.
* <p>
* 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<Item> 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.
* <p>
* 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();
}
}
}

View file

@ -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<T extends 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<Item> 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.
* <p>
* 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.
* <p>
* 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<FinishedRecipe> cc);
@Override
public final void build(Consumer<FinishedRecipe> cc) {
if (!checkRecipe()) return;
buildRecipe(cc);
}
@Override
public final ResourceLocation getId() {
return id;
}
}

View file

@ -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<T extends AbstractDoubleInputRecipeBuilder, R extends Recipe<? extends Container>> extends AbstractSingleInputRecipeBuilder<T, R> {
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<Item> input) {
this.secondaryInput = Ingredient.of(input);
return (T) this;
}
public T setSecondaryInputAndUnlock(TagKey<Item> 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));
}
}

View file

@ -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<T extends AbstractSimpleRecipe, C extends Container, R extends Recipe<C>> extends AbstractAdvancementRecipe {
public final ResourceLocation id;
protected String group;
protected Ingredient input;
protected ItemLike output;
protected final String category;
protected final RecipeType<R> type;
protected int count;
protected boolean exist;
protected AbstractSimpleRecipe(ResourceLocation id, RecipeType<R> type, ItemLike output) {
this(id, type, type.toString(), output);
}
protected AbstractSimpleRecipe(ResourceLocation id, RecipeType<R> 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<Item> 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);
}
}

View file

@ -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<T extends AbstractSimpleRecipeBuilder> extends AbstractBaseRecipeBuilder<T> {
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<Item> input) {
this.primaryInput = Ingredient.of(input);
return (T) this;
}
public T setPrimaryInputAndUnlock(TagKey<Item> 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();
}
}

View file

@ -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<T extends AbstractSingleInputRecipeBuilder, R extends Recipe<? extends Container>> extends AbstractSimpleRecipeBuilder<T> {
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<Item> 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<FinishedRecipe> cc) {
setupAdvancementForResult();
cc.accept(new AbstractSingleInputRecipeBuilder<T, R>.Result());
}
protected abstract RecipeSerializer<R> 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<R> 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);
}
}
}

View file

@ -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<T extends AbstractUnlockableRecipeBuilder> extends AbstractSimpleRecipeBuilder<T> {
protected AbstractUnlockableRecipeBuilder(ResourceLocation id, ItemLike output) {
super(id, output);
}
protected final Map<String, CriterionTriggerInstance> unlocks = new HashMap<>();
@Override
public T unlockedBy(ItemLike item) {
return super.unlockedBy(item);
}
@Override
public T unlockedBy(TagKey<Item> tag) {
return super.unlockedBy(tag);
}
@Override
protected T unlocks(String name, CriterionTriggerInstance trigger) {
this.unlocks.put(name, trigger);
return (T) this;
}
}

View file

@ -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<Container>, 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<Builder, AlloyingRecipe> {
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<Item> input) {
this.primaryInput = Ingredient.of(input);
return this;
}
public Builder setSecondaryInput(TagKey<Item> 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<Container>, 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<AlloyingRecipe> 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<Container>, 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!");
}

View file

@ -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<Container>, 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<Container>, UnknownReceipBookCategory
return "AnvilRecipe [" + id + "]";
}
public static class Builder {
private final static Builder INSTANCE = new Builder();
public static class Builder extends AbstractDoubleInputRecipeBuilder<Builder, AnvilRecipe> {
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<Item> 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<Container>, 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<Container>, UnknownReceipBookCategory
return this;
}
public Builder checkConfig(PathConfig config) {
exist &= config.getBoolean("anvil", id.getPath(), true);
return this;
@Override
protected RecipeSerializer<AnvilRecipe> 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<Container>, 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);

View file

@ -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);
}
}

View file

@ -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, Container, BlastingRecipe> {
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
);
}
}

View file

@ -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<T extends AbstractSimpleRecipe, C extends Container, R extends Recipe<C>> extends AbstractSimpleRecipe<T, C, R> {
protected float experience;
protected int cookingTime;
protected CookingBookCategory bookCategory;
CookingRecipe(ResourceLocation id, RecipeType<R> type, ItemLike output) {
this(id, type, type.toString(), output);
}
CookingRecipe(ResourceLocation id, RecipeType<R> 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<Item> 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;
}
}

View file

@ -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<CookingRecipeBuilder> {
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<Item> 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<FinishedRecipe> 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<FinishedRecipe> 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"
);
}
}
}

View file

@ -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<CraftingRecipeBuilder> {
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<String, CriterionTriggerInstance> unlocks = new HashMap<>();
protected final Map<Character, Ingredient> materials = new HashMap<>();
@Override
public CraftingRecipeBuilder setOutputCount(int count) {
return super.setOutputCount(count);
}
public CraftingRecipeBuilder addMaterial(char key, TagKey<Item> 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<Item> 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<FinishedRecipe> 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<FinishedRecipe> cc) {
final ShapelessRecipeBuilder builder = ShapelessRecipeBuilder.shapeless(
category, output.getItem(), output.getCount()
);
for (Map.Entry<String, CriterionTriggerInstance> item : unlocks.entrySet()) {
builder.unlockedBy(item.getKey(), item.getValue());
}
for (Map.Entry<Character, Ingredient> 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<FinishedRecipe> cc) {
final ShapedRecipeBuilder builder = ShapedRecipeBuilder.shaped(
category, output.getItem(), output.getCount()
);
for (Map.Entry<String, CriterionTriggerInstance> item : unlocks.entrySet()) {
builder.unlockedBy(item.getKey(), item.getValue());
}
for (Map.Entry<Character, Ingredient> 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;
}
}

View file

@ -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<Item> 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);
}
}
}

View file

@ -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<CraftingRecipe> type;
private boolean shaped;
private String[] shape;
private final Map<Character, Ingredient> materialKeys = Maps.newHashMap();
private final Map<Character, TagKey<Item>> 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<Item> 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<Ingredient> getMaterials(int width, int height) {
NonNullList<Ingredient> 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<GridRecipe> RECIPES;
public GridRecipe build() {
if (RECIPES == null) RECIPES = new ArrayList<>();
RECIPES.add(this);
return this;
}
public static void registerRecipes(Consumer<FinishedRecipe> cc) {
if (RECIPES == null) return;
for (var r : RECIPES) {
r.build(cc);
}
RECIPES.clear();
}
public void build(Consumer<FinishedRecipe> 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<Ingredient> 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<Character, Ingredient> 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<Character, TagKey<Item>> 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<Character, Ingredient> 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<Character, TagKey<Item>> 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);
}
}
}

View file

@ -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<SmithingRecipeBuilder> {
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<Item> 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<FinishedRecipe> 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);
}
}

View file

@ -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<SmithingTableRecipe, Container, SmithingRecipe> {
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<Item> 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<Item> 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));
}
}

View file

@ -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, Container, StonecutterRecipe> {
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<Item> in) {
return super.setInput(in);
}
@Override
protected StonecutterRecipe buildRecipe() {
return new StonecutterRecipe(id, group, input, new ItemStack(output, count));
}
}

View file

@ -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<StonecutterRecipeBuilder> {
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<Item> in) {
return super.setPrimaryInputAndUnlock(in);
}
@Override
public StonecutterRecipeBuilder setOutputCount(int count) {
return super.setOutputCount(count);
}
@Override
protected void buildRecipe(Consumer<FinishedRecipe> 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);
}
}

View file

@ -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;
}
}

View file

@ -129,4 +129,5 @@ public class JsonFactory {
JsonElement elem = object.get(member);
return elem == null ? def : elem.getAsString();
}
}

View file

@ -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<FinishedRecipe> exporter) {
GridRecipe.registerRecipes(exporter);
}
}

View file

@ -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": [