From c7e4e600527b26f50a772661a0fe393b81414fff Mon Sep 17 00:00:00 2001 From: Cyborgmas Date: Thu, 24 Mar 2022 18:08:17 -0400 Subject: [PATCH] 1.18 Allow mod menus to have their own recipebook (#8028) --- .../client/ClientRecipeBook.java.patch | 11 + .../client/RecipeBookCategories.java.patch | 38 +++ .../stats/RecipeBookSettings.java.patch | 21 ++ .../world/inventory/RecipeBookType.java.patch | 22 ++ .../client/RecipeBookRegistry.java | 53 ++++ .../RecipeBookExtensionClientHelper.java | 35 +++ .../recipebook/RecipeBookExtensionTest.java | 106 +++++++ .../recipe/recipebook/RecipeBookTestMenu.java | 292 ++++++++++++++++++ .../recipebook/RecipeBookTestRecipe.java | 204 ++++++++++++ .../RecipeBookTestRecipeSerializer.java | 40 +++ .../recipebook/RecipeBookTestScreen.java | 146 +++++++++ src/test/resources/META-INF/mods.toml | 2 + .../recipe_book_extension_advancment.json | 39 +++ .../recipe_book_extension_recipe_1.json | 26 ++ .../recipe_book_extension_recipe_2.json | 30 ++ .../recipe_book_extension_recipe_3.json | 25 ++ .../recipe_book_extension_recipe_4.json | 26 ++ .../recipe_book_extension_recipe_5.json | 22 ++ 18 files changed, 1138 insertions(+) create mode 100644 patches/minecraft/net/minecraft/client/ClientRecipeBook.java.patch create mode 100644 patches/minecraft/net/minecraft/client/RecipeBookCategories.java.patch create mode 100644 patches/minecraft/net/minecraft/stats/RecipeBookSettings.java.patch create mode 100644 patches/minecraft/net/minecraft/world/inventory/RecipeBookType.java.patch create mode 100644 src/main/java/net/minecraftforge/client/RecipeBookRegistry.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionClientHelper.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionTest.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestMenu.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipe.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipeSerializer.java create mode 100644 src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestScreen.java create mode 100644 src/test/resources/data/recipe_book_extension_test/advancements/recipes/recipe_book_extension_advancment.json create mode 100644 src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_1.json create mode 100644 src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_2.json create mode 100644 src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_3.json create mode 100644 src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_4.json create mode 100644 src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_5.json diff --git a/patches/minecraft/net/minecraft/client/ClientRecipeBook.java.patch b/patches/minecraft/net/minecraft/client/ClientRecipeBook.java.patch new file mode 100644 index 0000000000..cf29f1ae9c --- /dev/null +++ b/patches/minecraft/net/minecraft/client/ClientRecipeBook.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/client/ClientRecipeBook.java ++++ b/net/minecraft/client/ClientRecipeBook.java +@@ -104,6 +_,8 @@ + } else if (recipetype == RecipeType.f_44113_) { + return RecipeBookCategories.SMITHING; + } else { ++ RecipeBookCategories categories = net.minecraftforge.client.RecipeBookRegistry.findCategories(recipetype, p_90647_); ++ if (categories != null) return categories; + f_90618_.warn("Unknown recipe category: {}/{}", LogUtils.defer(() -> { + return Registry.f_122864_.m_7981_(p_90647_.m_6671_()); + }), LogUtils.defer(p_90647_::m_6423_)); diff --git a/patches/minecraft/net/minecraft/client/RecipeBookCategories.java.patch b/patches/minecraft/net/minecraft/client/RecipeBookCategories.java.patch new file mode 100644 index 0000000000..3ee67a58a7 --- /dev/null +++ b/patches/minecraft/net/minecraft/client/RecipeBookCategories.java.patch @@ -0,0 +1,38 @@ +--- a/net/minecraft/client/RecipeBookCategories.java ++++ b/net/minecraft/client/RecipeBookCategories.java +@@ -12,7 +_,7 @@ + import net.minecraftforge.api.distmarker.OnlyIn; + + @OnlyIn(Dist.CLIENT) +-public enum RecipeBookCategories { ++public enum RecipeBookCategories implements net.minecraftforge.common.IExtensibleEnum { + CRAFTING_SEARCH(new ItemStack(Items.f_42522_)), + CRAFTING_BUILDING_BLOCKS(new ItemStack(Blocks.f_50076_)), + CRAFTING_REDSTONE(new ItemStack(Items.f_42451_)), +@@ -36,7 +_,7 @@ + public static final List f_92257_ = ImmutableList.of(BLAST_FURNACE_SEARCH, BLAST_FURNACE_BLOCKS, BLAST_FURNACE_MISC); + public static final List f_92258_ = ImmutableList.of(FURNACE_SEARCH, FURNACE_FOOD, FURNACE_BLOCKS, FURNACE_MISC); + public static final List f_92259_ = ImmutableList.of(CRAFTING_SEARCH, CRAFTING_EQUIPMENT, CRAFTING_BUILDING_BLOCKS, CRAFTING_MISC, CRAFTING_REDSTONE); +- public static final Map> f_92260_ = ImmutableMap.of(CRAFTING_SEARCH, ImmutableList.of(CRAFTING_EQUIPMENT, CRAFTING_BUILDING_BLOCKS, CRAFTING_MISC, CRAFTING_REDSTONE), FURNACE_SEARCH, ImmutableList.of(FURNACE_FOOD, FURNACE_BLOCKS, FURNACE_MISC), BLAST_FURNACE_SEARCH, ImmutableList.of(BLAST_FURNACE_BLOCKS, BLAST_FURNACE_MISC), SMOKER_SEARCH, ImmutableList.of(SMOKER_FOOD)); ++ public static final Map> f_92260_ = net.minecraftforge.client.RecipeBookRegistry.AGGREGATE_CATEGORIES_VIEW; + private final List f_92261_; + + private RecipeBookCategories(ItemStack... p_92267_) { +@@ -54,11 +_,17 @@ + case SMOKER: + return f_92256_; + default: ++ if (net.minecraftforge.client.RecipeBookRegistry.TYPE_TO_CATEGORIES_VIEW.containsKey(p_92270_)) ++ return net.minecraftforge.client.RecipeBookRegistry.TYPE_TO_CATEGORIES_VIEW.get(p_92270_); + return ImmutableList.of(); + } + } + + public List m_92268_() { + return this.f_92261_; ++ } ++ ++ public static RecipeBookCategories create(String name, ItemStack... icons) { ++ throw new IllegalStateException("Enum not extended"); + } + } diff --git a/patches/minecraft/net/minecraft/stats/RecipeBookSettings.java.patch b/patches/minecraft/net/minecraft/stats/RecipeBookSettings.java.patch new file mode 100644 index 0000000000..6a9d71b0ff --- /dev/null +++ b/patches/minecraft/net/minecraft/stats/RecipeBookSettings.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/stats/RecipeBookSettings.java ++++ b/net/minecraft/stats/RecipeBookSettings.java +@@ -10,7 +_,7 @@ + import net.minecraft.world.inventory.RecipeBookType; + + public final class RecipeBookSettings { +- private static final Map> f_12725_ = ImmutableMap.of(RecipeBookType.CRAFTING, Pair.of("isGuiOpen", "isFilteringCraftable"), RecipeBookType.FURNACE, Pair.of("isFurnaceGuiOpen", "isFurnaceFilteringCraftable"), RecipeBookType.BLAST_FURNACE, Pair.of("isBlastingFurnaceGuiOpen", "isBlastingFurnaceFilteringCraftable"), RecipeBookType.SMOKER, Pair.of("isSmokerGuiOpen", "isSmokerFilteringCraftable")); ++ private static final Map> f_12725_ = new java.util.HashMap<>(ImmutableMap.of(RecipeBookType.CRAFTING, Pair.of("isGuiOpen", "isFilteringCraftable"), RecipeBookType.FURNACE, Pair.of("isFurnaceGuiOpen", "isFurnaceFilteringCraftable"), RecipeBookType.BLAST_FURNACE, Pair.of("isBlastingFurnaceGuiOpen", "isBlastingFurnaceFilteringCraftable"), RecipeBookType.SMOKER, Pair.of("isSmokerGuiOpen", "isSmokerFilteringCraftable"))); + private final Map f_12726_; + + private RecipeBookSettings(Map p_12730_) { +@@ -147,5 +_,9 @@ + public String toString() { + return "[open=" + this.f_12766_ + ", filtering=" + this.f_12767_ + "]"; + } ++ } ++ //FORGE -- called automatically on Enum creation - used for serialization ++ public static void addTagsForType(RecipeBookType type, String openTag, String filteringTag) { ++ f_12725_.put(type, Pair.of(openTag, filteringTag)); + } + } diff --git a/patches/minecraft/net/minecraft/world/inventory/RecipeBookType.java.patch b/patches/minecraft/net/minecraft/world/inventory/RecipeBookType.java.patch new file mode 100644 index 0000000000..2bd4395906 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/inventory/RecipeBookType.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/world/inventory/RecipeBookType.java ++++ b/net/minecraft/world/inventory/RecipeBookType.java +@@ -1,8 +_,18 @@ + package net.minecraft.world.inventory; + +-public enum RecipeBookType { ++public enum RecipeBookType implements net.minecraftforge.common.IExtensibleEnum { + CRAFTING, + FURNACE, + BLAST_FURNACE, + SMOKER; ++ ++ public static RecipeBookType create(String name) { ++ throw new IllegalStateException("Enum not extended!"); ++ } ++ ++ @Override ++ public void init() { ++ String name = this.name().toLowerCase(java.util.Locale.ROOT).replace("_",""); ++ net.minecraft.stats.RecipeBookSettings.addTagsForType(this, "is" + name + "GuiOpen", "is" + name + "FilteringCraftable"); ++ } + } diff --git a/src/main/java/net/minecraftforge/client/RecipeBookRegistry.java b/src/main/java/net/minecraftforge/client/RecipeBookRegistry.java new file mode 100644 index 0000000000..417109bc71 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/RecipeBookRegistry.java @@ -0,0 +1,53 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.client; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import net.minecraft.client.RecipeBookCategories; +import net.minecraft.world.inventory.RecipeBookType; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeType; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Function; + +import static net.minecraft.client.RecipeBookCategories.*; + +public class RecipeBookRegistry +{ + // The ImmutableMap is the patched out value of AGGREGATE_CATEGORIES + private static final Map> MUTABLE_AGGREGATE_CATEGORIES = new HashMap<>(ImmutableMap.of(CRAFTING_SEARCH, ImmutableList.of(CRAFTING_EQUIPMENT, CRAFTING_BUILDING_BLOCKS, CRAFTING_MISC, CRAFTING_REDSTONE), FURNACE_SEARCH, ImmutableList.of(FURNACE_FOOD, FURNACE_BLOCKS, FURNACE_MISC), BLAST_FURNACE_SEARCH, ImmutableList.of(BLAST_FURNACE_BLOCKS, BLAST_FURNACE_MISC), SMOKER_SEARCH, ImmutableList.of(SMOKER_FOOD))); + + private static final Map> TYPE_TO_CATEGORIES = new HashMap<>(); + private static final Map, Function, RecipeBookCategories>> FIND_CATEGORIES_FOR_TYPE = new HashMap<>(); + + //Unmodifiable view, the nested lists are immutable + public static final Map> AGGREGATE_CATEGORIES_VIEW = Collections.unmodifiableMap(MUTABLE_AGGREGATE_CATEGORIES); + public static final Map> TYPE_TO_CATEGORIES_VIEW = Collections.unmodifiableMap(TYPE_TO_CATEGORIES); + + public static void addCategoriesToType(RecipeBookType type, List categories) + { + TYPE_TO_CATEGORIES.put(type, categories instanceof ImmutableList ? categories : Collections.unmodifiableList(categories)); + } + + public static void addAggregateCategories(RecipeBookCategories search, List aggregate) + { + MUTABLE_AGGREGATE_CATEGORIES.put(search, aggregate instanceof ImmutableList ? aggregate : Collections.unmodifiableList(aggregate)); + } + + public static void addCategoriesFinder(RecipeType type, Function, RecipeBookCategories> finder) + { + FIND_CATEGORIES_FOR_TYPE.put(type, finder); + } + + @Nullable + public static RecipeBookCategories findCategories(RecipeType type, Recipe recipe) + { + return Optional.ofNullable(FIND_CATEGORIES_FOR_TYPE.get(type)).map(f -> f.apply(recipe)).orElse(null); + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionClientHelper.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionClientHelper.java new file mode 100644 index 0000000000..4ac2620e42 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionClientHelper.java @@ -0,0 +1,35 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import net.minecraft.client.RecipeBookCategories; +import net.minecraft.stats.RecipeBook; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraftforge.client.RecipeBookRegistry; + +import java.util.function.Supplier; + +public class RecipeBookExtensionClientHelper +{ + public static final Supplier TESTING_SEARCH = Suppliers.memoize(() -> RecipeBookCategories.create("TESTING_SEARCH", new ItemStack(Items.COMPASS))); + public static final Supplier TESTING_CAT_1 = Suppliers.memoize(() -> RecipeBookCategories.create("TESTING_CAT_1", new ItemStack(Items.DIAMOND))); + public static final Supplier TESTING_CAT_2 = Suppliers.memoize(() -> RecipeBookCategories.create("TESTING_CAT_2", new ItemStack(Items.NETHERITE_INGOT))); + + public static void init() + { + RecipeBookRegistry.addCategoriesToType(RecipeBookExtensionTest.TEST_TYPE, ImmutableList.of(TESTING_SEARCH.get(), TESTING_CAT_1.get(), TESTING_CAT_2.get())); + RecipeBookRegistry.addAggregateCategories(TESTING_SEARCH.get(), ImmutableList.of(TESTING_CAT_1.get(), TESTING_CAT_2.get())); + RecipeBookRegistry.addCategoriesFinder(RecipeBookTestRecipe.TYPE.get(), r -> + { + if (r.getResultItem().getItem() == Items.DIAMOND_BLOCK) + return TESTING_CAT_1.get(); + else return TESTING_CAT_2.get(); + }); + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionTest.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionTest.java new file mode 100644 index 0000000000..5b666cdda9 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookExtensionTest.java @@ -0,0 +1,106 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.RecipeBookType; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.crafting.conditions.ICondition; +import net.minecraftforge.common.crafting.conditions.IConditionSerializer; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.network.NetworkHooks; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegistryObject; + +@Mod(RecipeBookExtensionTest.MOD_ID) +public class RecipeBookExtensionTest +{ + public static final boolean ENABLED = false; + + public static final String MOD_ID = "recipe_book_extension_test"; + public static final RecipeBookType TEST_TYPE = RecipeBookType.create("TESTING"); + + public static final DeferredRegister> RECIPE_SERIALIZER = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, MOD_ID); + public static final RegistryObject> RECIPE_BOOK_TEST_RECIPE_SERIALIZER = + RECIPE_SERIALIZER.register("test_recipe", RecipeBookTestRecipeSerializer::new); + + public static final DeferredRegister> MENU_TYPE = DeferredRegister.create(ForgeRegistries.CONTAINERS, MOD_ID); + public static final RegistryObject> RECIPE_BOOK_TEST_MENU_TYPE = + MENU_TYPE.register("test_recipe_menu", () -> IForgeMenuType.create(RecipeBookTestMenu::new)); + + public RecipeBookExtensionTest() + { + if (!ENABLED) + return; + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::commonSetup); + RECIPE_SERIALIZER.register(FMLJavaModLoadingContext.get().getModEventBus()); + MENU_TYPE.register(FMLJavaModLoadingContext.get().getModEventBus()); + MinecraftForge.EVENT_BUS.addListener(this::onRightClick); + } + + private void commonSetup(FMLCommonSetupEvent event) + { + event.enqueueWork(() -> + { + RecipeType.register(getId("test_recipe").toString()); + }); + } + + private void onRightClick(PlayerInteractEvent.RightClickBlock event) + { + if (event.getWorld().isClientSide) + return; + if (event.getWorld().getBlockState(event.getPos()).getBlock() == Blocks.GRASS_BLOCK) + NetworkHooks.openGui((ServerPlayer) event.getPlayer(), new SimpleMenuProvider((id, inv, p) -> new RecipeBookTestMenu(id, inv, ContainerLevelAccess.create(event.getWorld(), event.getPos())), new TextComponent("Test"))); + } + + public static ResourceLocation getId(String name) + { + return new ResourceLocation(MOD_ID, name); + } + + @Mod.EventBusSubscriber(modid = MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) + public static class ClientHandler + { + @SubscribeEvent + public static void clientSetup(FMLClientSetupEvent event) + { + if (!ENABLED) + return; + event.enqueueWork(() -> + { + MenuScreens.register(RECIPE_BOOK_TEST_MENU_TYPE.get(), RecipeBookTestScreen::new); + RecipeBookExtensionClientHelper.init(); + }); + } + } + + public static class RecipeBookTestContainer extends SimpleContainer + { + public RecipeBookTestContainer() + { + super(8); + } + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestMenu.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestMenu.java new file mode 100644 index 0000000000..5f61ce9e94 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestMenu.java @@ -0,0 +1,292 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import net.minecraft.core.NonNullList; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.recipebook.ServerPlaceRecipe; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.*; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.debug.recipe.recipebook.RecipeBookExtensionTest.RecipeBookTestContainer; + +import java.util.Optional; + +public class RecipeBookTestMenu extends RecipeBookMenu +{ + private final RecipeBookTestContainer container = new RecipeBookTestContainer(); + private final ResultContainer resultContainer = new ResultContainer(); + private final Slot resultSlot; + private final ContainerLevelAccess access; + private final Player player; + + public RecipeBookTestMenu(int id, Inventory inv, FriendlyByteBuf extraData) + { + this(id, inv, ContainerLevelAccess.NULL); + } + + public RecipeBookTestMenu(int id, Inventory inv, ContainerLevelAccess access) + { + super(RecipeBookExtensionTest.RECIPE_BOOK_TEST_MENU_TYPE.get(), id); + this.access = access; + this.container.addListener(this::slotsChanged); + this.player = inv.player; + + /** + * Copied from {@link ResultSlot} but not limited to {@link CraftingContainer} + */ + this.resultSlot = new Slot(this.resultContainer, 0, 144, 35) + { + private int removeCount; + + @Override + public boolean mayPlace(ItemStack stack) + { + return false; + } + + @Override + public void onTake(Player player, ItemStack stack) + { + this.checkTakeAchievements(stack); + Container craftingContainer = RecipeBookTestMenu.this.container; + NonNullList remainders = player.level.getRecipeManager().getRemainingItemsFor(RecipeBookTestRecipe.TYPE.get(), RecipeBookTestMenu.this.container, player.level); + for (int i = 0; i < remainders.size(); ++i) + { + ItemStack toRemove = craftingContainer.getItem(i); + ItemStack toReplace = remainders.get(i); + if (!toRemove.isEmpty()) + { + craftingContainer.removeItem(i, 1); + toRemove = craftingContainer.getItem(i); + } + + if (!toReplace.isEmpty()) + { + if (toRemove.isEmpty()) + craftingContainer.setItem(i, toRemove); + else if (ItemStack.isSame(toRemove, toReplace) && ItemStack.tagMatches(toRemove, toReplace)) + { + toReplace.grow(toRemove.getCount()); + craftingContainer.setItem(i, toReplace); + } + else if (!player.getInventory().add(toReplace)) + player.drop(toReplace, false); + } + } + } + @Override + public ItemStack remove(int amount) + { + if (this.hasItem()) + this.removeCount += Math.min(amount, this.getItem().getCount()); + return super.remove(amount); + } + + @Override + public void onQuickCraft(ItemStack output, int amount) + { + this.removeCount += amount; + this.checkTakeAchievements(output); + } + + @Override + protected void onSwapCraft(int amount) + { + this.removeCount = amount; + } + + @Override + protected void checkTakeAchievements(ItemStack stack) + { + if (this.removeCount > 0) + stack.onCraftedBy(RecipeBookTestMenu.this.player.level, RecipeBookTestMenu.this.player, this.removeCount); + if (this.container instanceof RecipeHolder recipeHolder) + recipeHolder.awardUsedRecipes(RecipeBookTestMenu.this.player); + this.removeCount = 0; + } + }; + this.addSlot(this.resultSlot); //slot 0 + + //slots 1 - 8 + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 2; ++j) + this.addSlot(new Slot(this.container, j + i * 2, 30 + j * 18, 61 - i * 18)); + } + + //slots 9 to 35 + for(int i = 0; i < 3; ++i) + { + for(int j = 0; j < 9; ++j) + this.addSlot(new Slot(inv, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + + //slots 36 to 44 + for(int i = 0; i < 9; ++i) + this.addSlot(new Slot(inv, i, 8 + i * 18, 142)); + } + + @Override + public boolean stillValid(Player player) + { + return stillValid(this.access, player, Blocks.GRASS_BLOCK); + } + + @Override + public void removed(Player player) + { + super.removed(player); + this.access.execute((level, pos) -> this.clearContainer(player, this.container)); + } + + @Override + public boolean canTakeItemForPickAll(ItemStack stack, Slot slot) + { + return slot != resultSlot; + } + + @Override + public void slotsChanged(Container container) + { + this.access.execute((level, pos) -> + { + if (container == this.container) + { + Optional recipe = level.getRecipeManager() + .getRecipeFor(RecipeBookTestRecipe.TYPE.get(), this.container, level); + if (recipe.isEmpty()) + this.resultContainer.setItem(0, ItemStack.EMPTY); + else if (player instanceof ServerPlayer sp && this.resultContainer.setRecipeUsed(level, sp, recipe.get())) { + ItemStack stack = recipe.get().assemble(this.container); + this.resultContainer.setItem(0, stack); + } + } + }); + + super.slotsChanged(container); + } + + private static final int RESULT_SLOT = 0; + private static final int CRAFTING_START = 1; + private static final int CRAFTING_STOP = 8; + private static final int INVENTORY_START = 9; + private static final int INVENTORY_STOP = 44; + private static final int HOTBAR_START = 36; + + /** + * Mostly copied from {@link CraftingMenu#quickMoveStack} + */ + public ItemStack quickMoveStack(Player player, int idx) + { + ItemStack ret = ItemStack.EMPTY; + Slot slot = this.slots.get(idx); + if (!slot.hasItem()) + return ret; + + ItemStack item = slot.getItem(); + ret = item.copy(); + + if (idx == RESULT_SLOT) + { + if (!this.moveItemStackTo(item, INVENTORY_START, INVENTORY_STOP + 1, true)) + return ItemStack.EMPTY; + + slot.onQuickCraft(item, ret); + } + else if (idx >= INVENTORY_START && idx < INVENTORY_STOP + 1) + { + if (!this.moveItemStackTo(item, CRAFTING_START, CRAFTING_STOP + 1, false)) + { + if (idx < HOTBAR_START) + { + if (!this.moveItemStackTo(item, HOTBAR_START, INVENTORY_STOP + 1, false)) + return ItemStack.EMPTY; + } + else if (!this.moveItemStackTo(item, INVENTORY_START, HOTBAR_START, false)) + return ItemStack.EMPTY; + } + } + else if (!this.moveItemStackTo(item, INVENTORY_START, INVENTORY_STOP + 1, false)) + return ItemStack.EMPTY; + + if (item.isEmpty()) + slot.set(ItemStack.EMPTY); + else + slot.setChanged(); + + if (item.getCount() == ret.getCount()) + return ItemStack.EMPTY; + + slot.onTake(player, item); + if (idx == RESULT_SLOT) + player.drop(item, false); + + return ret; + } + + //RecipeBook stuff + @Override + public void fillCraftSlotsStackedContents(StackedContents contents) + { + this.container.fillStackedContents(contents); + } + + @Override + public void clearCraftingContent() + { + this.container.clearContent(); + this.resultContainer.clearContent(); + } + + @Override + public boolean recipeMatches(Recipe recipe) + { + return recipe.matches(this.container, this.player.level); + } + + @Override + public int getResultSlotIndex() + { + return RESULT_SLOT; + } + + @Override + public int getGridWidth() + { + return 2; + } + + @Override + public int getGridHeight() + { + return 4; + } + + @Override + public int getSize() + { + return 9; + } + + @Override + public RecipeBookType getRecipeBookType() + { + return RecipeBookExtensionTest.TEST_TYPE; + } + + @Override + public boolean shouldMoveToInventory(int slotIdx) + { + return slotIdx != RESULT_SLOT; + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipe.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipe.java new file mode 100644 index 0000000000..d79c6d4c20 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipe.java @@ -0,0 +1,204 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.gson.JsonElement; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.NonNullList; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +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.RecipeType; +import net.minecraft.world.item.crafting.ShapedRecipe; +import net.minecraft.world.level.Level; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class RecipeBookTestRecipe implements Recipe +{ + @SuppressWarnings("unchecked") + public static final Supplier> TYPE = Suppliers.memoize(() -> (RecipeType) Registry.RECIPE_TYPE.get(RecipeBookExtensionTest.getId("test_recipe"))); + + public final ResourceLocation id; + public final Ingredients ingredients; + private final int width; + private final int height; + private final NonNullList items; + + public RecipeBookTestRecipe(ResourceLocation id, Ingredients ingredients) + { + this.id = id; + this.ingredients = ingredients; + this.width = ingredients.pattern.get(0).length(); + this.height = ingredients.pattern.size(); + List pattern = new ArrayList<>(ingredients.pattern); //might need to reverse this list. + while (pattern.size() != 4) + pattern.add(" "); + this.items = pattern.stream() + .flatMap(s -> Stream.of(s.substring(0,1), s.substring(1, 2))) + .map(s -> s.equals(" ") ? Ingredient.EMPTY : ingredients.recipe.get(s)) + .peek(i -> Objects.requireNonNull(i, "A key in sculpting pattern was not defined!")) + .collect(Collectors.toCollection(NonNullList::create)); + } + + /** + * Taken from {@link ShapedRecipe} + */ + @Override + public boolean matches(RecipeBookExtensionTest.RecipeBookTestContainer container, Level level) + { + for (int i = 0; i <= 2 - this.width; ++i) + { + for (int j = 0; j <= 4 - this.height; ++j) + { + if (this.matches(container, i, j, true) || this.matches(container, i, j, false)) + return true; + } + } + + return false; + } + + private boolean matches(RecipeBookExtensionTest.RecipeBookTestContainer container, int x, int y, boolean mirror) //unsure about the last boolean + { + for (int i = 0; i < 2; ++i) + { + for (int j = 0; j < 4; ++j) + { + int curX = i - x; + int curY = j - y; + Ingredient ingredient = Ingredient.EMPTY; + if (curX >= 0 && curY >= 0 && curX < this.width && curY < this.height) + { + int idx = mirror ? this.width - curX - 1 + curY * this.width : curX + curY * this.width; + ingredient = this.items.get(idx); + } + + if (!ingredient.test(container.getItem(i + j * 2))) + return false; + } + } + + return true; + } + + @Override + public ItemStack assemble(RecipeBookExtensionTest.RecipeBookTestContainer p_44001_) + { + return this.getResultItem().copy(); + } + + @Override + public boolean canCraftInDimensions(int p_43999_, int p_44000_) + { + return this.width <= p_43999_ && this.height <= p_44000_; //used for recipe book + } + + @Override + public ItemStack getResultItem() + { + return this.ingredients.result(); + } + + @Override + public ResourceLocation getId() + { + return this.id; + } + + @Override + public RecipeSerializer getSerializer() + { + return RecipeBookExtensionTest.RECIPE_BOOK_TEST_RECIPE_SERIALIZER.get(); + } + + @Override + public RecipeType getType() + { + return TYPE.get(); + } + + @Override + public String getGroup() + { + return this.ingredients.group; + } + + @Override + public NonNullList getIngredients() + { + return this.items; + } + + @Override + public boolean isIncomplete() + { + return this.getIngredients().isEmpty() || + this.getIngredients().stream() + .filter((ingredient) -> !ingredient.isEmpty()) + .anyMatch(i -> i.getItems().length == 0); + } + + @Override + public ItemStack getToastSymbol() + { + return new ItemStack(Items.NETHERITE_BLOCK); + } + + public record Ingredients(String group, List pattern, Map recipe, ItemStack result) + { + private static final Function> VERIFY_LENGTH_2 = s -> s.length() == 2 ? DataResult.success(s) : + DataResult.error("Key row length must be of 2!"); + private static final Function, DataResult>> VERIFY_SIZE = l -> + { + if (l.size() <= 4 && l.size() >= 1) + { + List temp = new ArrayList<>(l); + Collections.reverse(temp); //reverse so the first row is at the bottom in the json. + return DataResult.success(ImmutableList.copyOf(temp)); + } + return DataResult.error("Pattern must have between 1 and 4 rows of keys"); + }; + private static final Function> VERIFY_LENGTH_1 = s -> s.length() == 1 ? DataResult.success(s) : + DataResult.error("Key must be a single character!"); + + public static final Codec INGREDIENT_CODEC = Codec.PASSTHROUGH.comapFlatMap(obj -> + { + JsonElement json = obj.convert(JsonOps.INSTANCE).getValue(); + try + { + return DataResult.success(Ingredient.fromJson(json)); + } + catch (Exception e) + { + return DataResult.error("Failed to parse ingredient: " + e.getMessage()); + } + }, ingredient -> new Dynamic<>(JsonOps.INSTANCE, ingredient.toJson())); + + public static final Codec CODEC = RecordCodecBuilder.create(inst -> + inst.group( + Codec.STRING.fieldOf("group").forGetter(Ingredients::group), + Codec.STRING.flatXmap(VERIFY_LENGTH_2, VERIFY_LENGTH_2).listOf().flatXmap(VERIFY_SIZE, VERIFY_SIZE).fieldOf("pattern").forGetter(Ingredients::pattern), + Codec.unboundedMap(Codec.STRING.flatXmap(VERIFY_LENGTH_1, VERIFY_LENGTH_1), INGREDIENT_CODEC).fieldOf("key").forGetter(Ingredients::recipe), + ItemStack.CODEC.fieldOf("result").forGetter(Ingredients::result) + ).apply(inst, Ingredients::new) + ); + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipeSerializer.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipeSerializer.java new file mode 100644 index 0000000000..9069a9e213 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestRecipeSerializer.java @@ -0,0 +1,40 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import com.google.gson.JsonObject; +import com.mojang.serialization.JsonOps; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraftforge.debug.recipe.recipebook.RecipeBookTestRecipe.Ingredients; +import net.minecraftforge.registries.ForgeRegistryEntry; + +import javax.annotation.Nullable; + +public class RecipeBookTestRecipeSerializer extends ForgeRegistryEntry> implements RecipeSerializer +{ + @Override + public RecipeBookTestRecipe fromJson(ResourceLocation id, JsonObject json) + { + Ingredients ingredients = Ingredients.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(false, s->{}); + return new RecipeBookTestRecipe(id, ingredients); + } + + @Nullable + @Override + public RecipeBookTestRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buf) + { + Ingredients ingredients = buf.readWithCodec(Ingredients.CODEC); + return new RecipeBookTestRecipe(id, ingredients); + } + + @Override + public void toNetwork(FriendlyByteBuf buffer, RecipeBookTestRecipe recipe) + { + buffer.writeWithCodec(Ingredients.CODEC, recipe.ingredients); + } +} diff --git a/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestScreen.java b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestScreen.java new file mode 100644 index 0000000000..0e4f60521a --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/recipe/recipebook/RecipeBookTestScreen.java @@ -0,0 +1,146 @@ +/* + * Minecraft Forge - Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.recipe.recipebook; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.inventory.CraftingScreen; +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; +import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.locale.Language; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.Slot; + +/** + * Mostly copied from {@link CraftingScreen} + */ +public class RecipeBookTestScreen extends AbstractContainerScreen implements RecipeUpdateListener +{ + private static final ResourceLocation TEXTURE = RecipeBookExtensionTest.getId("textures/gui/container/recipe_book_test.png"); + private static final ResourceLocation RECIPE_BUTTON_LOCATION = new ResourceLocation("textures/gui/recipe_button.png"); + private final RecipeBookComponent recipeBookComponent = new RecipeBookComponent(); + private boolean widthTooNarrow; + + public RecipeBookTestScreen(RecipeBookTestMenu menu, Inventory inv, Component title) + { + super(menu, inv, title); + } + + @Override + protected void init() + { + super.init(); + this.widthTooNarrow = this.width < 379; + this.titleLabelX = this.imageWidth - this.font.width(Language.getInstance().getVisualOrder(this.title)) - 5; + this.recipeBookComponent.init(this.width, this.height, this.minecraft, this.widthTooNarrow, this.menu); + this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth); + this.addRenderableWidget(new ImageButton(this.leftPos + 5, this.height / 2 - 49, 20, 18, 0, 0, 19, RECIPE_BUTTON_LOCATION, (button) -> + { + this.recipeBookComponent.toggleVisibility(); + this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth); + ((ImageButton)button).setPosition(this.leftPos + 5, this.height / 2 - 49); + })); + this.addWidget(this.recipeBookComponent); + this.setInitialFocus(this.recipeBookComponent); + } + + @Override + protected void containerTick() + { + super.containerTick(); + this.recipeBookComponent.tick(); + } + + @Override + public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) + { + this.renderBackground(stack); + if (this.recipeBookComponent.isVisible() && this.widthTooNarrow) + { + this.renderBg(stack, partialTicks, mouseX, mouseY); + this.recipeBookComponent.render(stack, mouseX, mouseY, partialTicks); + } + else + { + this.recipeBookComponent.render(stack, mouseX, mouseY, partialTicks); + super.render(stack, mouseX, mouseY, partialTicks); + this.recipeBookComponent.renderGhostRecipe(stack, this.leftPos, this.topPos, true, partialTicks); + } + this.renderTooltip(stack, mouseX, mouseY); + this.recipeBookComponent.renderTooltip(stack, this.leftPos, this.topPos, mouseX, mouseY); + } + + @Override + protected void renderBg(PoseStack stack, float partialTicks, int mouseX, int mouseY) + { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, TEXTURE); + int x = this.leftPos; + int y = this.topPos; + this.blit(stack, x, y, 0, 0, this.imageWidth, this.imageHeight); + } + + /** + * Color gotten from super + */ + @Override + protected void renderLabels(PoseStack stack, int mouseX, int mouseY) + { + this.font.draw(stack, this.title, this.titleLabelX, this.titleLabelY, 4210752); + } + + protected boolean isHovering(int x, int y, int width, int height, double mouseX, double mouseY) + { + return (!this.widthTooNarrow || !this.recipeBookComponent.isVisible()) && super.isHovering(x, y, width, height, mouseX, mouseY); + } + + public boolean mouseClicked(double mouseX, double mouseY, int buttonId) + { + if (this.recipeBookComponent.mouseClicked(mouseX, mouseY, buttonId)) + { + this.setFocused(this.recipeBookComponent); + return true; + } + return this.widthTooNarrow && this.recipeBookComponent.isVisible() || super.mouseClicked(mouseX, mouseY, buttonId); + } + + protected boolean hasClickedOutside(double mouseX, double mouseY, int x, int y, int buttonIdx) + { + boolean flag = mouseX < (double)x || mouseY < (double)y || mouseX >= (double)(x + this.imageWidth) || mouseY >= (double)(y + this.imageHeight); + return flag && this.recipeBookComponent.hasClickedOutside(mouseX, mouseY, this.leftPos, this.topPos, this.imageWidth, this.imageHeight, buttonIdx); + } + + protected void slotClicked(Slot slot, int mouseX, int mouseY, ClickType clickType) + { + super.slotClicked(slot, mouseX, mouseY, clickType); + this.recipeBookComponent.slotClicked(slot); + } + + @Override + public void recipesUpdated() + { + this.recipeBookComponent.recipesUpdated(); + } + + @Override + public void removed() + { + this.recipeBookComponent.removed(); + super.removed(); + } + + @Override + public RecipeBookComponent getRecipeBookComponent() + { + return this.recipeBookComponent; + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 1233f85629..b2cb62e422 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -208,6 +208,8 @@ license="LGPL v2.1" modId="many_mob_effects_test" [[mods]] modId="potion_size_event_test" +[[mods]] + modId="recipe_book_extension_test" [[mods]] modId="hide_neighbor_face_test" [[mods]] diff --git a/src/test/resources/data/recipe_book_extension_test/advancements/recipes/recipe_book_extension_advancment.json b/src/test/resources/data/recipe_book_extension_test/advancements/recipes/recipe_book_extension_advancment.json new file mode 100644 index 0000000000..2ae1eecfdf --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/advancements/recipes/recipe_book_extension_advancment.json @@ -0,0 +1,39 @@ +{ + "parent": "minecraft:recipes/root", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "rewards": { + "recipes": [ + "recipe_book_extension_test:recipe_book_extension_recipe_1" + ] + }, + "criteria": { + "has_cobblestone": { + "trigger": "minecraft:inventory_changed", + "conditions": { + "items": [ + { + "items": [ + "minecraft:cobblestone" + ] + } + ] + } + }, + "has_the_recipe": { + "trigger": "minecraft:recipe_unlocked", + "conditions": { + "recipe": "recipe_book_extension_test:recipe_book_extension_recipe_1" + } + } + }, + "requirements": [ + [ + "has_cobblestone", + "has_the_recipe" + ] + ] +} \ No newline at end of file diff --git a/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_1.json b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_1.json new file mode 100644 index 0000000000..80c0451305 --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_1.json @@ -0,0 +1,26 @@ +{ + "type": "recipe_book_extension_test:test_recipe", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "result": { + "id": "diamond_block", + "Count" : 1 + }, + "group": "test_1", + "pattern": [ + "T ", + "T#", + "# " + ], + "key": { + "#": { + "item": "minecraft:cobblestone" + }, + "T": { + "item": "minecraft:stone" + } + } +} \ No newline at end of file diff --git a/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_2.json b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_2.json new file mode 100644 index 0000000000..ad58d31f6c --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_2.json @@ -0,0 +1,30 @@ +{ + "type": "recipe_book_extension_test:test_recipe", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "result": { + "id": "diamond_block", + "Count" : 1 + }, + "group": "test_1", + "pattern": [ + "TA", + "T#", + "TT", + "A " + ], + "key": { + "#": { + "item": "minecraft:cobblestone" + }, + "T": { + "item": "minecraft:stone" + }, + "A" : { + "item" : "gold_ingot" + } + } +} \ No newline at end of file diff --git a/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_3.json b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_3.json new file mode 100644 index 0000000000..24bc1bf637 --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_3.json @@ -0,0 +1,25 @@ +{ + "type": "recipe_book_extension_test:test_recipe", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "result": { + "id": "netherite_block", + "Count" : 1 + }, + "group": "test_2", + "pattern": [ + "TA", + "A " + ], + "key": { + "T": { + "item": "minecraft:stone" + }, + "A" : { + "item" : "gold_ingot" + } + } +} \ No newline at end of file diff --git a/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_4.json b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_4.json new file mode 100644 index 0000000000..c431ec4360 --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_4.json @@ -0,0 +1,26 @@ +{ + "type": "recipe_book_extension_test:test_recipe", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "result": { + "id": "netherite_block", + "Count" : 1 + }, + "group": "test_2", + "pattern": [ + "TT", + "AA", + "TT" + ], + "key": { + "T": { + "item": "minecraft:cobblestone" + }, + "A" : { + "item" : "gold_ingot" + } + } +} \ No newline at end of file diff --git a/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_5.json b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_5.json new file mode 100644 index 0000000000..3af105c49a --- /dev/null +++ b/src/test/resources/data/recipe_book_extension_test/recipes/recipe_book_extension_recipe_5.json @@ -0,0 +1,22 @@ +{ + "type": "recipe_book_extension_test:test_recipe", + "conditions" : [ + { + "type" : "forge:false" + } + ], + "result": { + "id": "stone", + "Count" : 1 + }, + "group": "test_2", + "pattern": [ + "AA", + "AA" + ], + "key": { + "A" : { + "item" : "gold_ingot" + } + } +} \ No newline at end of file