package net.minecraft.data.recipes; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.advancements.Criterion; import net.minecraft.advancements.critereon.InventoryChangeTrigger; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.MinMaxBounds; import net.minecraft.advancements.critereon.EnterBlockTrigger.TriggerInstance; import net.minecraft.advancements.critereon.InventoryChangeTrigger.TriggerInstance.Slots; import net.minecraft.core.HolderGetter; import net.minecraft.core.HolderLookup; import net.minecraft.core.Holder.Reference; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.data.BlockFamilies; import net.minecraft.data.BlockFamily; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.data.BlockFamily.Variant; import net.minecraft.data.PackOutput.PathProvider; import net.minecraft.data.recipes.RecipeProvider.Runner.1; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.AbstractCookingRecipe; import net.minecraft.world.item.crafting.BlastingRecipe; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.SmeltingRecipe; import net.minecraft.world.item.crafting.AbstractCookingRecipe.Factory; import net.minecraft.world.item.equipment.trim.TrimPattern; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SuspiciousEffectHolder; import org.jetbrains.annotations.Nullable; public abstract class RecipeProvider { protected final HolderLookup.Provider registries; private final HolderGetter items; protected final RecipeOutput output; private static final Map SHAPE_BUILDERS = ImmutableMap.builder() .put(Variant.BUTTON, (recipeProvider, itemLike, itemLike2) -> recipeProvider.buttonBuilder(itemLike, Ingredient.of(itemLike2))) .put( Variant.CHISELED, (recipeProvider, itemLike, itemLike2) -> recipeProvider.chiseledBuilder(RecipeCategory.BUILDING_BLOCKS, itemLike, Ingredient.of(itemLike2)) ) .put(Variant.CUT, (recipeProvider, itemLike, itemLike2) -> recipeProvider.cutBuilder(RecipeCategory.BUILDING_BLOCKS, itemLike, Ingredient.of(itemLike2))) .put(Variant.DOOR, (recipeProvider, itemLike, itemLike2) -> recipeProvider.doorBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.CUSTOM_FENCE, (recipeProvider, itemLike, itemLike2) -> recipeProvider.fenceBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.FENCE, (recipeProvider, itemLike, itemLike2) -> recipeProvider.fenceBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.CUSTOM_FENCE_GATE, (recipeProvider, itemLike, itemLike2) -> recipeProvider.fenceGateBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.FENCE_GATE, (recipeProvider, itemLike, itemLike2) -> recipeProvider.fenceGateBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.SIGN, (recipeProvider, itemLike, itemLike2) -> recipeProvider.signBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.SLAB, (recipeProvider, itemLike, itemLike2) -> recipeProvider.slabBuilder(RecipeCategory.BUILDING_BLOCKS, itemLike, Ingredient.of(itemLike2))) .put(Variant.STAIRS, (recipeProvider, itemLike, itemLike2) -> recipeProvider.stairBuilder(itemLike, Ingredient.of(itemLike2))) .put( Variant.PRESSURE_PLATE, (recipeProvider, itemLike, itemLike2) -> recipeProvider.pressurePlateBuilder(RecipeCategory.REDSTONE, itemLike, Ingredient.of(itemLike2)) ) .put( Variant.POLISHED, (recipeProvider, itemLike, itemLike2) -> recipeProvider.polishedBuilder(RecipeCategory.BUILDING_BLOCKS, itemLike, Ingredient.of(itemLike2)) ) .put(Variant.TRAPDOOR, (recipeProvider, itemLike, itemLike2) -> recipeProvider.trapdoorBuilder(itemLike, Ingredient.of(itemLike2))) .put(Variant.WALL, (recipeProvider, itemLike, itemLike2) -> recipeProvider.wallBuilder(RecipeCategory.DECORATIONS, itemLike, Ingredient.of(itemLike2))) .build(); protected RecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { this.registries = registries; this.items = registries.lookupOrThrow(Registries.ITEM); this.output = output; } protected abstract void buildRecipes(); protected void generateForEnabledBlockFamilies(FeatureFlagSet enabledFeatures) { BlockFamilies.getAllFamilies().filter(BlockFamily::shouldGenerateRecipe).forEach(blockFamily -> this.generateRecipes(blockFamily, enabledFeatures)); } protected void oneToOneConversionRecipe(ItemLike result, ItemLike ingredient, @Nullable String group) { this.oneToOneConversionRecipe(result, ingredient, group, 1); } protected void oneToOneConversionRecipe(ItemLike result, ItemLike ingredient, @Nullable String group, int resultCount) { this.shapeless(RecipeCategory.MISC, result, resultCount) .requires(ingredient) .group(group) .unlockedBy(getHasName(ingredient), this.has(ingredient)) .save(this.output, getConversionRecipeName(result, ingredient)); } protected void oreSmelting(List ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group) { this.oreCooking(RecipeSerializer.SMELTING_RECIPE, SmeltingRecipe::new, ingredients, category, result, experience, cookingTime, group, "_from_smelting"); } protected void oreBlasting(List ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group) { this.oreCooking(RecipeSerializer.BLASTING_RECIPE, BlastingRecipe::new, ingredients, category, result, experience, cookingTime, group, "_from_blasting"); } private void oreCooking( RecipeSerializer serializer, Factory recipeFactory, List ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group, String suffix ) { for (ItemLike itemLike : ingredients) { SimpleCookingRecipeBuilder.generic(Ingredient.of(itemLike), category, result, experience, cookingTime, serializer, recipeFactory) .group(group) .unlockedBy(getHasName(itemLike), this.has(itemLike)) .save(this.output, getItemName(result) + suffix + "_" + getItemName(itemLike)); } } protected void netheriteSmithing(Item ingredientItem, RecipeCategory category, Item resultItem) { SmithingTransformRecipeBuilder.smithing( Ingredient.of(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE), Ingredient.of(ingredientItem), this.tag(ItemTags.NETHERITE_TOOL_MATERIALS), category, resultItem ) .unlocks("has_netherite_ingot", this.has(ItemTags.NETHERITE_TOOL_MATERIALS)) .save(this.output, getItemName(resultItem) + "_smithing"); } protected void trimSmithing(Item template, ResourceKey pattern, ResourceKey> recipe) { Reference reference = this.registries.lookupOrThrow(Registries.TRIM_PATTERN).getOrThrow(pattern); SmithingTrimRecipeBuilder.smithingTrim( Ingredient.of(template), this.tag(ItemTags.TRIMMABLE_ARMOR), this.tag(ItemTags.TRIM_MATERIALS), reference, RecipeCategory.MISC ) .unlocks("has_smithing_trim_template", this.has(template)) .save(this.output, recipe); } protected void twoByTwoPacker(RecipeCategory category, ItemLike packed, ItemLike unpacked) { this.shaped(category, packed, 1).define('#', unpacked).pattern("##").pattern("##").unlockedBy(getHasName(unpacked), this.has(unpacked)).save(this.output); } protected void threeByThreePacker(RecipeCategory category, ItemLike packed, ItemLike unpacked, String criterionName) { this.shapeless(category, packed).requires(unpacked, 9).unlockedBy(criterionName, this.has(unpacked)).save(this.output); } protected void threeByThreePacker(RecipeCategory category, ItemLike packed, ItemLike unpacked) { this.threeByThreePacker(category, packed, unpacked, getHasName(unpacked)); } protected void planksFromLog(ItemLike planks, TagKey logs, int resultCount) { this.shapeless(RecipeCategory.BUILDING_BLOCKS, planks, resultCount).requires(logs).group("planks").unlockedBy("has_log", this.has(logs)).save(this.output); } protected void planksFromLogs(ItemLike planks, TagKey logs, int result) { this.shapeless(RecipeCategory.BUILDING_BLOCKS, planks, result).requires(logs).group("planks").unlockedBy("has_logs", this.has(logs)).save(this.output); } protected void woodFromLogs(ItemLike wood, ItemLike log) { this.shaped(RecipeCategory.BUILDING_BLOCKS, wood, 3) .define('#', log) .pattern("##") .pattern("##") .group("bark") .unlockedBy("has_log", this.has(log)) .save(this.output); } protected void woodenBoat(ItemLike boat, ItemLike material) { this.shaped(RecipeCategory.TRANSPORTATION, boat) .define('#', material) .pattern("# #") .pattern("###") .group("boat") .unlockedBy("in_water", insideOf(Blocks.WATER)) .save(this.output); } protected void chestBoat(ItemLike boat, ItemLike material) { this.shapeless(RecipeCategory.TRANSPORTATION, boat) .requires(Blocks.CHEST) .requires(material) .group("chest_boat") .unlockedBy("has_boat", this.has(ItemTags.BOATS)) .save(this.output); } private RecipeBuilder buttonBuilder(ItemLike button, Ingredient material) { return this.shapeless(RecipeCategory.REDSTONE, button).requires(material); } protected RecipeBuilder doorBuilder(ItemLike door, Ingredient material) { return this.shaped(RecipeCategory.REDSTONE, door, 3).define('#', material).pattern("##").pattern("##").pattern("##"); } private RecipeBuilder fenceBuilder(ItemLike fence, Ingredient material) { int i = fence == Blocks.NETHER_BRICK_FENCE ? 6 : 3; Item item = fence == Blocks.NETHER_BRICK_FENCE ? Items.NETHER_BRICK : Items.STICK; return this.shaped(RecipeCategory.DECORATIONS, fence, i).define('W', material).define('#', item).pattern("W#W").pattern("W#W"); } private RecipeBuilder fenceGateBuilder(ItemLike fenceGate, Ingredient material) { return this.shaped(RecipeCategory.REDSTONE, fenceGate).define('#', Items.STICK).define('W', material).pattern("#W#").pattern("#W#"); } protected void pressurePlate(ItemLike pressurePlate, ItemLike material) { this.pressurePlateBuilder(RecipeCategory.REDSTONE, pressurePlate, Ingredient.of(material)) .unlockedBy(getHasName(material), this.has(material)) .save(this.output); } private RecipeBuilder pressurePlateBuilder(RecipeCategory category, ItemLike pressurePlate, Ingredient material) { return this.shaped(category, pressurePlate).define('#', material).pattern("##"); } protected void slab(RecipeCategory category, ItemLike slab, ItemLike material) { this.slabBuilder(category, slab, Ingredient.of(material)).unlockedBy(getHasName(material), this.has(material)).save(this.output); } protected RecipeBuilder slabBuilder(RecipeCategory category, ItemLike slab, Ingredient material) { return this.shaped(category, slab, 6).define('#', material).pattern("###"); } protected RecipeBuilder stairBuilder(ItemLike stairs, Ingredient material) { return this.shaped(RecipeCategory.BUILDING_BLOCKS, stairs, 4).define('#', material).pattern("# ").pattern("## ").pattern("###"); } protected RecipeBuilder trapdoorBuilder(ItemLike trapdoor, Ingredient material) { return this.shaped(RecipeCategory.REDSTONE, trapdoor, 2).define('#', material).pattern("###").pattern("###"); } private RecipeBuilder signBuilder(ItemLike sign, Ingredient material) { return this.shaped(RecipeCategory.DECORATIONS, sign, 3) .group("sign") .define('#', material) .define('X', Items.STICK) .pattern("###") .pattern("###") .pattern(" X "); } protected void hangingSign(ItemLike sign, ItemLike material) { this.shaped(RecipeCategory.DECORATIONS, sign, 6) .group("hanging_sign") .define('#', material) .define('X', Items.CHAIN) .pattern("X X") .pattern("###") .pattern("###") .unlockedBy("has_stripped_logs", this.has(material)) .save(this.output); } protected void colorBlockWithDye(List dyes, List dyeableItems, String group) { this.colorWithDye(dyes, dyeableItems, null, group, RecipeCategory.BUILDING_BLOCKS); } protected void colorWithDye(List dyes, List dyeableItems, @Nullable Item dye, String group, RecipeCategory category) { for (int i = 0; i < dyes.size(); i++) { Item item = (Item)dyes.get(i); Item item2 = (Item)dyeableItems.get(i); Stream stream = dyeableItems.stream().filter(item2x -> !item2x.equals(item2)); if (dye != null) { stream = Stream.concat(stream, Stream.of(dye)); } this.shapeless(category, item2) .requires(item) .requires(Ingredient.of(stream)) .group(group) .unlockedBy("has_needed_dye", this.has(item)) .save(this.output, "dye_" + getItemName(item2)); } } protected void carpet(ItemLike carpet, ItemLike material) { this.shaped(RecipeCategory.DECORATIONS, carpet, 3) .define('#', material) .pattern("##") .group("carpet") .unlockedBy(getHasName(material), this.has(material)) .save(this.output); } protected void bedFromPlanksAndWool(ItemLike bed, ItemLike wool) { this.shaped(RecipeCategory.DECORATIONS, bed) .define('#', wool) .define('X', ItemTags.PLANKS) .pattern("###") .pattern("XXX") .group("bed") .unlockedBy(getHasName(wool), this.has(wool)) .save(this.output); } protected void banner(ItemLike banner, ItemLike material) { this.shaped(RecipeCategory.DECORATIONS, banner) .define('#', material) .define('|', Items.STICK) .pattern("###") .pattern("###") .pattern(" | ") .group("banner") .unlockedBy(getHasName(material), this.has(material)) .save(this.output); } protected void stainedGlassFromGlassAndDye(ItemLike stainedGlass, ItemLike dye) { this.shaped(RecipeCategory.BUILDING_BLOCKS, stainedGlass, 8) .define('#', Blocks.GLASS) .define('X', dye) .pattern("###") .pattern("#X#") .pattern("###") .group("stained_glass") .unlockedBy("has_glass", this.has(Blocks.GLASS)) .save(this.output); } protected void stainedGlassPaneFromStainedGlass(ItemLike stainedGlassPane, ItemLike stainedGlass) { this.shaped(RecipeCategory.DECORATIONS, stainedGlassPane, 16) .define('#', stainedGlass) .pattern("###") .pattern("###") .group("stained_glass_pane") .unlockedBy("has_glass", this.has(stainedGlass)) .save(this.output); } protected void stainedGlassPaneFromGlassPaneAndDye(ItemLike stainedGlassPane, ItemLike dye) { this.shaped(RecipeCategory.DECORATIONS, stainedGlassPane, 8) .define('#', Blocks.GLASS_PANE) .define('$', dye) .pattern("###") .pattern("#$#") .pattern("###") .group("stained_glass_pane") .unlockedBy("has_glass_pane", this.has(Blocks.GLASS_PANE)) .unlockedBy(getHasName(dye), this.has(dye)) .save(this.output, getConversionRecipeName(stainedGlassPane, Blocks.GLASS_PANE)); } protected void coloredTerracottaFromTerracottaAndDye(ItemLike terracotta, ItemLike dye) { this.shaped(RecipeCategory.BUILDING_BLOCKS, terracotta, 8) .define('#', Blocks.TERRACOTTA) .define('X', dye) .pattern("###") .pattern("#X#") .pattern("###") .group("stained_terracotta") .unlockedBy("has_terracotta", this.has(Blocks.TERRACOTTA)) .save(this.output); } protected void concretePowder(ItemLike concretePowder, ItemLike dye) { this.shapeless(RecipeCategory.BUILDING_BLOCKS, concretePowder, 8) .requires(dye) .requires(Blocks.SAND, 4) .requires(Blocks.GRAVEL, 4) .group("concrete_powder") .unlockedBy("has_sand", this.has(Blocks.SAND)) .unlockedBy("has_gravel", this.has(Blocks.GRAVEL)) .save(this.output); } protected void candle(ItemLike candle, ItemLike dye) { this.shapeless(RecipeCategory.DECORATIONS, candle) .requires(Blocks.CANDLE) .requires(dye) .group("dyed_candle") .unlockedBy(getHasName(dye), this.has(dye)) .save(this.output); } protected void wall(RecipeCategory category, ItemLike wall, ItemLike material) { this.wallBuilder(category, wall, Ingredient.of(material)).unlockedBy(getHasName(material), this.has(material)).save(this.output); } private RecipeBuilder wallBuilder(RecipeCategory category, ItemLike wall, Ingredient material) { return this.shaped(category, wall, 6).define('#', material).pattern("###").pattern("###"); } protected void polished(RecipeCategory category, ItemLike result, ItemLike material) { this.polishedBuilder(category, result, Ingredient.of(material)).unlockedBy(getHasName(material), this.has(material)).save(this.output); } private RecipeBuilder polishedBuilder(RecipeCategory category, ItemLike result, Ingredient material) { return this.shaped(category, result, 4).define('S', material).pattern("SS").pattern("SS"); } protected void cut(RecipeCategory category, ItemLike cutResult, ItemLike material) { this.cutBuilder(category, cutResult, Ingredient.of(material)).unlockedBy(getHasName(material), this.has(material)).save(this.output); } private ShapedRecipeBuilder cutBuilder(RecipeCategory category, ItemLike cutResult, Ingredient material) { return this.shaped(category, cutResult, 4).define('#', material).pattern("##").pattern("##"); } protected void chiseled(RecipeCategory category, ItemLike chiseledResult, ItemLike material) { this.chiseledBuilder(category, chiseledResult, Ingredient.of(material)).unlockedBy(getHasName(material), this.has(material)).save(this.output); } protected void mosaicBuilder(RecipeCategory category, ItemLike result, ItemLike material) { this.shaped(category, result).define('#', material).pattern("#").pattern("#").unlockedBy(getHasName(material), this.has(material)).save(this.output); } protected ShapedRecipeBuilder chiseledBuilder(RecipeCategory category, ItemLike chiseledResult, Ingredient material) { return this.shaped(category, chiseledResult).define('#', material).pattern("#").pattern("#"); } protected void stonecutterResultFromBase(RecipeCategory category, ItemLike result, ItemLike material) { this.stonecutterResultFromBase(category, result, material, 1); } protected void stonecutterResultFromBase(RecipeCategory category, ItemLike result, ItemLike material, int resultCount) { SingleItemRecipeBuilder.stonecutting(Ingredient.of(material), category, result, resultCount) .unlockedBy(getHasName(material), this.has(material)) .save(this.output, getConversionRecipeName(result, material) + "_stonecutting"); } private void smeltingResultFromBase(ItemLike result, ItemLike ingredient) { SimpleCookingRecipeBuilder.smelting(Ingredient.of(ingredient), RecipeCategory.BUILDING_BLOCKS, result, 0.1F, 200) .unlockedBy(getHasName(ingredient), this.has(ingredient)) .save(this.output); } protected void nineBlockStorageRecipes(RecipeCategory unpackedCategory, ItemLike unpacked, RecipeCategory packedCategory, ItemLike packed) { this.nineBlockStorageRecipes(unpackedCategory, unpacked, packedCategory, packed, getSimpleRecipeName(packed), null, getSimpleRecipeName(unpacked), null); } protected void nineBlockStorageRecipesWithCustomPacking( RecipeCategory unpackedCategory, ItemLike unpacked, RecipeCategory packedCategory, ItemLike packed, String packedName, String packedGroup ) { this.nineBlockStorageRecipes(unpackedCategory, unpacked, packedCategory, packed, packedName, packedGroup, getSimpleRecipeName(unpacked), null); } protected void nineBlockStorageRecipesRecipesWithCustomUnpacking( RecipeCategory unpackedCategory, ItemLike unpacked, RecipeCategory packedCategory, ItemLike packed, String unpackedName, String unpackedGroup ) { this.nineBlockStorageRecipes(unpackedCategory, unpacked, packedCategory, packed, getSimpleRecipeName(packed), null, unpackedName, unpackedGroup); } private void nineBlockStorageRecipes( RecipeCategory unpackedCategory, ItemLike unpacked, RecipeCategory packedCategory, ItemLike packed, String packedName, @Nullable String packedGroup, String unpackedName, @Nullable String unpackedGroup ) { this.shapeless(unpackedCategory, unpacked, 9) .requires(packed) .group(unpackedGroup) .unlockedBy(getHasName(packed), this.has(packed)) .save(this.output, ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(unpackedName))); this.shaped(packedCategory, packed) .define('#', unpacked) .pattern("###") .pattern("###") .pattern("###") .group(packedGroup) .unlockedBy(getHasName(unpacked), this.has(unpacked)) .save(this.output, ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(packedName))); } protected void copySmithingTemplate(ItemLike template, ItemLike baseItem) { this.shaped(RecipeCategory.MISC, template, 2) .define('#', Items.DIAMOND) .define('C', baseItem) .define('S', template) .pattern("#S#") .pattern("#C#") .pattern("###") .unlockedBy(getHasName(template), this.has(template)) .save(this.output); } protected void copySmithingTemplate(ItemLike template, Ingredient baseItem) { this.shaped(RecipeCategory.MISC, template, 2) .define('#', Items.DIAMOND) .define('C', baseItem) .define('S', template) .pattern("#S#") .pattern("#C#") .pattern("###") .unlockedBy(getHasName(template), this.has(template)) .save(this.output); } protected void cookRecipes( String cookingMethod, RecipeSerializer cookingSerializer, Factory recipeFactory, int cookingTime ) { this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.BEEF, Items.COOKED_BEEF, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.CHICKEN, Items.COOKED_CHICKEN, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.COD, Items.COOKED_COD, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.KELP, Items.DRIED_KELP, 0.1F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.SALMON, Items.COOKED_SALMON, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.MUTTON, Items.COOKED_MUTTON, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.PORKCHOP, Items.COOKED_PORKCHOP, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.POTATO, Items.BAKED_POTATO, 0.35F); this.simpleCookingRecipe(cookingMethod, cookingSerializer, recipeFactory, cookingTime, Items.RABBIT, Items.COOKED_RABBIT, 0.35F); } private void simpleCookingRecipe( String cookingMethod, RecipeSerializer cookingSerializer, Factory recipeFactory, int cookingTime, ItemLike material, ItemLike result, float experience ) { SimpleCookingRecipeBuilder.generic(Ingredient.of(material), RecipeCategory.FOOD, result, experience, cookingTime, cookingSerializer, recipeFactory) .unlockedBy(getHasName(material), this.has(material)) .save(this.output, getItemName(result) + "_from_" + cookingMethod); } protected void waxRecipes(FeatureFlagSet requiredFeatures) { ((BiMap)HoneycombItem.WAXABLES.get()) .forEach( (block, block2) -> { if (block2.requiredFeatures().isSubsetOf(requiredFeatures)) { this.shapeless(RecipeCategory.BUILDING_BLOCKS, block2) .requires(block) .requires(Items.HONEYCOMB) .group(getItemName(block2)) .unlockedBy(getHasName(block), this.has(block)) .save(this.output, getConversionRecipeName(block2, Items.HONEYCOMB)); } } ); } protected void grate(Block grateBlock, Block material) { this.shaped(RecipeCategory.BUILDING_BLOCKS, grateBlock, 4) .define('M', material) .pattern(" M ") .pattern("M M") .pattern(" M ") .unlockedBy(getHasName(material), this.has(material)) .save(this.output); } protected void copperBulb(Block bulbBlock, Block material) { this.shaped(RecipeCategory.REDSTONE, bulbBlock, 4) .define('C', material) .define('R', Items.REDSTONE) .define('B', Items.BLAZE_ROD) .pattern(" C ") .pattern("CBC") .pattern(" R ") .unlockedBy(getHasName(material), this.has(material)) .save(this.output); } protected void suspiciousStew(Item flowerItem, SuspiciousEffectHolder effect) { ItemStack itemStack = new ItemStack( Items.SUSPICIOUS_STEW.builtInRegistryHolder(), 1, DataComponentPatch.builder().set(DataComponents.SUSPICIOUS_STEW_EFFECTS, effect.getSuspiciousEffects()).build() ); this.shapeless(RecipeCategory.FOOD, itemStack) .requires(Items.BOWL) .requires(Items.BROWN_MUSHROOM) .requires(Items.RED_MUSHROOM) .requires(flowerItem) .group("suspicious_stew") .unlockedBy(getHasName(flowerItem), this.has(flowerItem)) .save(this.output, getItemName(itemStack.getItem()) + "_from_" + getItemName(flowerItem)); } protected void generateRecipes(BlockFamily blockFamily, FeatureFlagSet requiredFeatures) { blockFamily.getVariants().forEach((variant, block) -> { if (block.requiredFeatures().isSubsetOf(requiredFeatures)) { RecipeProvider.FamilyRecipeProvider familyRecipeProvider = (RecipeProvider.FamilyRecipeProvider)SHAPE_BUILDERS.get(variant); ItemLike itemLike = this.getBaseBlock(blockFamily, variant); if (familyRecipeProvider != null) { RecipeBuilder recipeBuilder = familyRecipeProvider.create(this, block, itemLike); blockFamily.getRecipeGroupPrefix().ifPresent(string -> recipeBuilder.group(string + (variant == Variant.CUT ? "" : "_" + variant.getRecipeGroup()))); recipeBuilder.unlockedBy((String)blockFamily.getRecipeUnlockedBy().orElseGet(() -> getHasName(itemLike)), this.has(itemLike)); recipeBuilder.save(this.output); } if (variant == Variant.CRACKED) { this.smeltingResultFromBase(block, itemLike); } } }); } private Block getBaseBlock(BlockFamily family, Variant variant) { if (variant == Variant.CHISELED) { if (!family.getVariants().containsKey(Variant.SLAB)) { throw new IllegalStateException("Slab is not defined for the family."); } else { return family.get(Variant.SLAB); } } else { return family.getBaseBlock(); } } private static Criterion insideOf(Block block) { return CriteriaTriggers.ENTER_BLOCK.createCriterion(new TriggerInstance(Optional.empty(), Optional.of(block.builtInRegistryHolder()), Optional.empty())); } private Criterion has(MinMaxBounds.Ints count, ItemLike item) { return inventoryTrigger(ItemPredicate.Builder.item().of(this.items, item).withCount(count)); } protected Criterion has(ItemLike itemLike) { return inventoryTrigger(ItemPredicate.Builder.item().of(this.items, itemLike)); } protected Criterion has(TagKey tag) { return inventoryTrigger(ItemPredicate.Builder.item().of(this.items, tag)); } private static Criterion inventoryTrigger(ItemPredicate.Builder... items) { return inventoryTrigger((ItemPredicate[])Arrays.stream(items).map(ItemPredicate.Builder::build).toArray(ItemPredicate[]::new)); } private static Criterion inventoryTrigger(ItemPredicate... predicates) { return CriteriaTriggers.INVENTORY_CHANGED.createCriterion(new InventoryChangeTrigger.TriggerInstance(Optional.empty(), Slots.ANY, List.of(predicates))); } protected static String getHasName(ItemLike itemLike) { return "has_" + getItemName(itemLike); } protected static String getItemName(ItemLike itemLike) { return BuiltInRegistries.ITEM.getKey(itemLike.asItem()).getPath(); } protected static String getSimpleRecipeName(ItemLike itemLike) { return getItemName(itemLike); } protected static String getConversionRecipeName(ItemLike result, ItemLike ingredient) { return getItemName(result) + "_from_" + getItemName(ingredient); } protected static String getSmeltingRecipeName(ItemLike itemLike) { return getItemName(itemLike) + "_from_smelting"; } protected static String getBlastingRecipeName(ItemLike itemLike) { return getItemName(itemLike) + "_from_blasting"; } protected Ingredient tag(TagKey tag) { return Ingredient.of(this.items.getOrThrow(tag)); } protected ShapedRecipeBuilder shaped(RecipeCategory category, ItemLike result) { return ShapedRecipeBuilder.shaped(this.items, category, result); } protected ShapedRecipeBuilder shaped(RecipeCategory category, ItemLike result, int count) { return ShapedRecipeBuilder.shaped(this.items, category, result, count); } protected ShapelessRecipeBuilder shapeless(RecipeCategory category, ItemStack result) { return ShapelessRecipeBuilder.shapeless(this.items, category, result); } protected ShapelessRecipeBuilder shapeless(RecipeCategory category, ItemLike result) { return ShapelessRecipeBuilder.shapeless(this.items, category, result); } protected ShapelessRecipeBuilder shapeless(RecipeCategory category, ItemLike result, int count) { return ShapelessRecipeBuilder.shapeless(this.items, category, result, count); } @FunctionalInterface interface FamilyRecipeProvider { RecipeBuilder create(RecipeProvider recipeProvider, ItemLike itemLike, ItemLike itemLike2); } protected abstract static class Runner implements DataProvider { private final PackOutput packOutput; private final CompletableFuture registries; protected Runner(PackOutput packOutput, CompletableFuture registries) { this.packOutput = packOutput; this.registries = registries; } @Override public final CompletableFuture run(CachedOutput output) { return this.registries.thenCompose(provider -> { PathProvider pathProvider = this.packOutput.createRegistryElementsPathProvider(Registries.RECIPE); PathProvider pathProvider2 = this.packOutput.createRegistryElementsPathProvider(Registries.ADVANCEMENT); Set>> set = Sets.>>newHashSet(); List> list = new ArrayList(); RecipeOutput recipeOutput = new 1(this, set, list, output, provider, pathProvider, pathProvider2); this.createRecipeProvider(provider, recipeOutput).buildRecipes(); return CompletableFuture.allOf((CompletableFuture[])list.toArray(CompletableFuture[]::new)); }); } protected abstract RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output); } }