diff --git a/paper-server/patches/features_unapplied/0031-Improve-exact-choice-recipe-ingredients.patch b/paper-server/patches/features/0032-Improve-exact-choice-recipe-ingredients.patch similarity index 53% rename from paper-server/patches/features_unapplied/0031-Improve-exact-choice-recipe-ingredients.patch rename to paper-server/patches/features/0032-Improve-exact-choice-recipe-ingredients.patch index 3822f7c7ffb7..60ecc236c6a2 100644 --- a/paper-server/patches/features_unapplied/0031-Improve-exact-choice-recipe-ingredients.patch +++ b/paper-server/patches/features/0032-Improve-exact-choice-recipe-ingredients.patch @@ -8,13 +8,14 @@ and shapeless recipes. diff --git a/io/papermc/paper/inventory/recipe/ItemOrExact.java b/io/papermc/paper/inventory/recipe/ItemOrExact.java new file mode 100644 -index 0000000000000000000000000000000000000000..ce745e49cd54fe3ae187785563a1bd311d14b5b2 +index 0000000000000000000000000000000000000000..9e621873454cc2edc2e59e6c7554d8116cd32bc1 --- /dev/null +++ b/io/papermc/paper/inventory/recipe/ItemOrExact.java -@@ -0,0 +1,63 @@ +@@ -0,0 +1,64 @@ +package io.papermc.paper.inventory.recipe; + +import net.minecraft.core.Holder; ++import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemStack; + +public sealed interface ItemOrExact permits ItemOrExact.Item, ItemOrExact.Exact { @@ -26,12 +27,12 @@ index 0000000000000000000000000000000000000000..ce745e49cd54fe3ae187785563a1bd31 + record Item(Holder item) implements ItemOrExact { + + public Item(final ItemStack stack) { -+ this(stack.getItemHolder()); ++ this(stack.typeHolder()); + } + + @Override + public int getMaxStackSize() { -+ return this.item.value().getDefaultMaxStackSize(); ++ return this.item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 1); + } + + @Override @@ -150,119 +151,119 @@ index 0000000000000000000000000000000000000000..f47c12e9dd6cfa857ca07a764edc22de + } +} diff --git a/net/minecraft/recipebook/ServerPlaceRecipe.java b/net/minecraft/recipebook/ServerPlaceRecipe.java -index 6d3e3ec045d5b15a435f7217369968b33e082724..b7a3758af337270737041f84d10eb43784e42279 100644 +index 31f8aa58b48aeb64e57480490a449b6c46913880..b7ee9fe694ea37eeb58b5dded7967e7d0f338d0d 100644 --- a/net/minecraft/recipebook/ServerPlaceRecipe.java +++ b/net/minecraft/recipebook/ServerPlaceRecipe.java -@@ -40,6 +40,7 @@ public class ServerPlaceRecipe> { +@@ -41,6 +41,7 @@ public class ServerPlaceRecipe> { return RecipeBookMenu.PostPlaceAction.NOTHING; } else { - StackedItemContents stackedItemContents = new StackedItemContents(); -+ stackedItemContents.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients - inventory.fillStackedContents(stackedItemContents); - menu.fillCraftSlotsStackedContents(stackedItemContents); - return serverPlaceRecipe.tryPlaceRecipe(recipe, stackedItemContents); -@@ -99,7 +100,7 @@ public class ServerPlaceRecipe> { + StackedItemContents availableItems = new StackedItemContents(); ++ availableItems.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients + inventory.fillStackedContents(availableItems); + menu.fillCraftSlotsStackedContents(availableItems); + return placer.tryPlaceRecipe(recipe, availableItems); +@@ -100,7 +101,7 @@ public class ServerPlaceRecipe> { } - int i = this.calculateAmountToCraft(biggestCraftableStack, flag); -- List> list = new ArrayList<>(); -+ List list = new ArrayList<>(); // Paper - Improve exact choice recipe ingredients - if (stackedItemContents.canCraft(recipe.value(), i, list::add)) { - int i1 = clampToMaxStackSize(i, list); - if (i1 != i) { -@@ -114,7 +115,7 @@ public class ServerPlaceRecipe> { - this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotsToIngredientIndex(), (item1, slot1, x, y) -> { - if (item1 != -1) { - Slot slot2 = this.inputGridSlots.get(slot1); -- Holder holder = list.get(item1); -+ io.papermc.paper.inventory.recipe.ItemOrExact holder = list.get(item1); // Paper - Improve exact choice recipe ingredients - int i2 = i1; + int amountToCraft = this.calculateAmountToCraft(biggestCraftableStack, recipeMatchesPlaced); +- List> itemsUsedPerIngredient = new ArrayList<>(); ++ List itemsUsedPerIngredient = new ArrayList<>(); // Paper - Improve exact choice recipe ingredients + if (availableItems.canCraft(recipe.value(), amountToCraft, itemsUsedPerIngredient::add)) { + int adjustedAmountToCraft = clampToMaxStackSize(amountToCraft, itemsUsedPerIngredient); + if (adjustedAmountToCraft != amountToCraft) { +@@ -119,7 +120,7 @@ public class ServerPlaceRecipe> { + (ingredientIndex, gridIndex, gridXPos, gridYPos) -> { + if (ingredientIndex != -1) { + Slot targetGridSlot = this.inputGridSlots.get(gridIndex); +- Holder itemUsed = itemsUsedPerIngredient.get(ingredientIndex); ++ io.papermc.paper.inventory.recipe.ItemOrExact itemUsed = itemsUsedPerIngredient.get(ingredientIndex); // Paper - Improve exact choice recipe ingredients + int remainingCount = adjustedAmountToCraft; - while (i2 > 0) { -@@ -129,9 +130,11 @@ public class ServerPlaceRecipe> { + while (remainingCount > 0) { +@@ -134,9 +135,11 @@ public class ServerPlaceRecipe> { } } -- private static int clampToMaxStackSize(int amount, List> items) { -- for (Holder holder : items) { -- amount = Math.min(amount, holder.value().getDefaultMaxStackSize()); +- private static int clampToMaxStackSize(int value, final List> items) { +- for (Holder item : items) { +- value = Math.min(value, item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 1)); + // Paper start - Improve exact choice recipe ingredients -+ private static int clampToMaxStackSize(int amount, List items) { -+ for (io.papermc.paper.inventory.recipe.ItemOrExact holder : items) { -+ amount = Math.min(amount, holder.getMaxStackSize()); ++ private static int clampToMaxStackSize(int value, final List items) { ++ for (io.papermc.paper.inventory.recipe.ItemOrExact item : items) { ++ value = Math.min(value, item.getMaxStackSize()); + // Paper end - Improve exact choice recipe ingredients } - return amount; -@@ -160,7 +163,7 @@ public class ServerPlaceRecipe> { + return value; +@@ -165,7 +168,7 @@ public class ServerPlaceRecipe> { } } -- private int moveItemToGrid(Slot slot, Holder item, int count) { -+ private int moveItemToGrid(Slot slot, io.papermc.paper.inventory.recipe.ItemOrExact item, int count) { // Paper - Improve exact choice recipe ingredients - ItemStack item1 = slot.getItem(); - int i = this.inventory.findSlotMatchingCraftingIngredient(item, item1); - if (i == -1) { +- private int moveItemToGrid(final Slot targetSlot, final Holder itemInInventory, final int count) { ++ private int moveItemToGrid(final Slot targetSlot, final io.papermc.paper.inventory.recipe.ItemOrExact itemInInventory, final int count) { // Paper - Improve exact choice recipe ingredients + ItemStack itemInTargetSlot = targetSlot.getItem(); + int inventorySlotId = this.inventory.findSlotMatchingCraftingIngredient(itemInInventory, itemInTargetSlot); + if (inventorySlotId == Inventory.NOT_FOUND_INDEX) { diff --git a/net/minecraft/world/entity/player/Inventory.java b/net/minecraft/world/entity/player/Inventory.java -index 76eda3bd8c513c7e42ceedf1aa605fb6df8b7019..41e59f3739945ca7f6ab710c993b5c0f15fcd529 100644 +index da8bab81e135a9b240fbeac0e9e1d2aa12c6212d..f004911c438e6f89d6e079437e3f4104d6cbacbd 100644 --- a/net/minecraft/world/entity/player/Inventory.java +++ b/net/minecraft/world/entity/player/Inventory.java @@ -252,12 +252,12 @@ public class Inventory implements Container, Nameable { - return !stack.isDamaged() && !stack.isEnchanted() && !stack.has(DataComponents.CUSTOM_NAME); + return !item.isDamaged() && !item.isEnchanted() && !item.has(DataComponents.CUSTOM_NAME); } -- public int findSlotMatchingCraftingIngredient(Holder item, ItemStack stack) { -+ public int findSlotMatchingCraftingIngredient(io.papermc.paper.inventory.recipe.ItemOrExact item, ItemStack stack) { // Paper - Improve exact choice recipe ingredients +- public int findSlotMatchingCraftingIngredient(final Holder item, final ItemStack existingItem) { ++ public int findSlotMatchingCraftingIngredient(final io.papermc.paper.inventory.recipe.ItemOrExact item, final ItemStack existingItem) { // Paper - Improve exact choice recipe ingredients for (int i = 0; i < this.items.size(); i++) { - ItemStack itemStack = this.items.get(i); - if (!itemStack.isEmpty() -- && itemStack.is(item) -- && isUsableForCrafting(itemStack) -+ && item.matches(itemStack) // Paper - Improve exact choice recipe ingredients -+ && (!(item instanceof io.papermc.paper.inventory.recipe.ItemOrExact.Item) || Inventory.isUsableForCrafting(itemStack)) // Paper - Improve exact choice recipe ingredients - && (stack.isEmpty() || ItemStack.isSameItemSameComponents(stack, itemStack))) { + ItemStack inventoryItemStack = this.items.get(i); + if (!inventoryItemStack.isEmpty() +- && inventoryItemStack.is(item) +- && isUsableForCrafting(inventoryItemStack) ++ && item.matches(inventoryItemStack) // Paper - Improve exact choice recipe ingredients ++ && (!(item instanceof io.papermc.paper.inventory.recipe.ItemOrExact.Item) || Inventory.isUsableForCrafting(inventoryItemStack)) // Paper - Improve exact choice recipe ingredients + && (existingItem.isEmpty() || ItemStack.isSameItemSameComponents(existingItem, inventoryItemStack))) { return i; } diff --git a/net/minecraft/world/entity/player/StackedContents.java b/net/minecraft/world/entity/player/StackedContents.java -index bc4f7d16365dffc87b1eef8aa5791a5cb595ee78..cbb37480c19b210cf83999247c1d670c9df2d150 100644 +index 889b8e6c3762203deccf657b505f6f29cc893526..6710653d033a48c4b3fc267496f93a2548b5c966 100644 --- a/net/minecraft/world/entity/player/StackedContents.java +++ b/net/minecraft/world/entity/player/StackedContents.java -@@ -13,7 +13,7 @@ import java.util.List; +@@ -14,7 +14,7 @@ import java.util.Objects; import org.jspecify.annotations.Nullable; public class StackedContents { - public final Reference2IntOpenHashMap amounts = new Reference2IntOpenHashMap<>(); + public final it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap amounts = new it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap<>(); // Paper - Improve exact choice recipe ingredients (don't use "reference" map) - boolean hasAtLeast(T item, int amount) { - return this.amounts.getInt(item) >= amount; -@@ -49,7 +49,7 @@ public class StackedContents { - List getUniqueAvailableIngredientItems(Iterable> ingredients) { - List list = new ArrayList<>(); + private boolean hasAtLeast(final T item, final int count) { + return this.amounts.getInt(item) >= count; +@@ -54,7 +54,7 @@ public class StackedContents { + private List getUniqueAvailableIngredientItems(final Iterable> ingredients) { + List result = new ArrayList<>(); -- for (Entry entry : Reference2IntMaps.fastIterable(this.amounts)) { -+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry entry : it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts)) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map) - if (entry.getIntValue() > 0 && anyIngredientMatches(ingredients, entry.getKey())) { - list.add(entry.getKey()); +- for (Entry availableItem : Reference2IntMaps.fastIterable(this.amounts)) { ++ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry availableItem : it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts)) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map) + if (availableItem.getIntValue() > 0 && anyIngredientMatches(ingredients, availableItem.getKey())) { + result.add(availableItem.getKey()); } -@@ -71,13 +71,13 @@ public class StackedContents { +@@ -76,13 +76,13 @@ public class StackedContents { @VisibleForTesting - public int getResultUpperBound(List> ingredients) { - int i = Integer.MAX_VALUE; -- ObjectIterable> objectIterable = Reference2IntMaps.fastIterable(this.amounts); -+ ObjectIterable> objectIterable = it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts); // Paper - Improve exact choice recipe ingredients (don't use "reference" map) + public int getResultUpperBound(final List> ingredients) { + int min = Integer.MAX_VALUE; +- ObjectIterable> availableItems = Reference2IntMaps.fastIterable(this.amounts); ++ ObjectIterable> availableItems = it.unimi.dsi.fastutil.objects.Object2IntMaps.fastIterable(this.amounts); // Paper - Improve exact choice recipe ingredients (don't use "reference" map) label31: - for (StackedContents.IngredientInfo ingredientInfo : ingredients) { - int i1 = 0; + for (StackedContents.IngredientInfo ingredient : ingredients) { + int max = 0; -- for (Entry entry : objectIterable) { -+ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry entry : objectIterable) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map) - int intValue = entry.getIntValue(); - if (intValue > i1) { - if (ingredientInfo.acceptsItem(entry.getKey())) { +- for (Entry entry : availableItems) { ++ for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry entry : availableItems) { // Paper - Improve exact choice recipe ingredients (don't use "reference" map) + int itemCount = entry.getIntValue(); + if (itemCount > max) { + if (ingredient.acceptsItem(entry.getKey())) { diff --git a/net/minecraft/world/entity/player/StackedItemContents.java b/net/minecraft/world/entity/player/StackedItemContents.java -index e0121e9c8439e32b7ef064c06942ccd5dd74987d..00dbc0d125b2c8c208ffa46f9084c6317efaba90 100644 +index ffaa8e450a11a01060b6847d1b66a76323f9d8ba..1ace17d8fc7e7b8b4722a1889d49233c0ec55e20 100644 --- a/net/minecraft/world/entity/player/StackedItemContents.java +++ b/net/minecraft/world/entity/player/StackedItemContents.java @@ -9,9 +9,27 @@ import net.minecraft.world.item.crafting.Recipe; @@ -289,115 +290,119 @@ index e0121e9c8439e32b7ef064c06942ccd5dd74987d..00dbc0d125b2c8c208ffa46f9084c631 + } + // Paper end - Improve exact choice recipe ingredients - public void accountSimpleStack(ItemStack stack) { -+ if (this.extrasMap != null && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; max of 64 due to accountStack method below - if (Inventory.isUsableForCrafting(stack)) { - this.accountStack(stack); + public void accountSimpleStack(final ItemStack itemStack) { ++ if (this.extrasMap != null && this.extrasMap.accountStack(itemStack, Math.min(itemStack.getMaxStackSize(), itemStack.getCount()))) return; // Paper - Improve exact choice recipe ingredients; Referenced from the accountStack method below + if (Inventory.isUsableForCrafting(itemStack)) { + this.accountStack(itemStack); } -@@ -24,34 +42,35 @@ public class StackedItemContents { - public void accountStack(ItemStack stack, int maxStackSize) { - if (!stack.isEmpty()) { - int min = Math.min(maxStackSize, stack.getCount()); -- this.raw.account(stack.getItemHolder(), min); -+ if (this.extrasMap != null && !stack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(stack, min)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it -+ this.raw.account(new io.papermc.paper.inventory.recipe.ItemOrExact.Item(stack.getItemHolder()), min); +@@ -24,38 +42,39 @@ public class StackedItemContents { + public void accountStack(final ItemStack itemStack, final int maxCount) { + if (!itemStack.isEmpty()) { + int count = Math.min(maxCount, itemStack.getCount()); +- this.raw.account(itemStack.typeHolder(), count); ++ if (this.extrasMap != null && !itemStack.getComponentsPatch().isEmpty() && this.extrasMap.accountStack(itemStack, count)) return; // Paper - Improve exact choice recipe ingredients; if an exact ingredient, don't include it ++ this.raw.account(new io.papermc.paper.inventory.recipe.ItemOrExact.Item(itemStack.typeHolder()), count); } } -- public boolean canCraft(Recipe recipe, StackedContents.@Nullable Output> output) { -+ public boolean canCraft(Recipe recipe, StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients +- public boolean canCraft(final Recipe recipe, final StackedContents.@Nullable Output> output) { ++ public boolean canCraft(final Recipe recipe, final StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients return this.canCraft(recipe, 1, output); } -- public boolean canCraft(Recipe recipe, int maxCount, StackedContents.@Nullable Output> output) { -+ public boolean canCraft(Recipe recipe, int maxCount, StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients +- public boolean canCraft(final Recipe recipe, final int amount, final StackedContents.@Nullable Output> output) { ++ public boolean canCraft(final Recipe recipe, final int amount, final StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients PlacementInfo placementInfo = recipe.placementInfo(); - return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), maxCount, output); + return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), amount, output); } -- public boolean canCraft(List>> ingredients, StackedContents.@Nullable Output> output) { -+ public boolean canCraft(List> ingredients, StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients - return this.canCraft(ingredients, 1, output); + public boolean canCraft( +- final List>> contents, final StackedContents.@Nullable Output> output ++ final List> contents, final StackedContents.@Nullable Output output // Paper - Improve exact choice recipe ingredients + ) { + return this.canCraft(contents, 1, output); } private boolean canCraft( -- List>> ingredients, int maxCount, StackedContents.@Nullable Output> output -+ List> ingredients, int maxCount, StackedContents.@Nullable Output output // Paper - Improve exact choice recipe ingredients +- final List>> contents, ++ final List> contents, // Paper - Improve exact choice recipe ingredients + final int amount, +- final StackedContents.@Nullable Output> output ++ final StackedContents.@Nullable Output output // Paper - Improve exact choice recipe ingredients ) { - return this.raw.tryPick(ingredients, maxCount, output); + return this.raw.tryPick(contents, amount, output); } -- public int getBiggestCraftableStack(Recipe recipe, StackedContents.@Nullable Output> output) { -+ public int getBiggestCraftableStack(Recipe recipe, StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients +- public int getBiggestCraftableStack(final Recipe recipe, final StackedContents.@Nullable Output> output) { ++ public int getBiggestCraftableStack(final Recipe recipe, final StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients return this.getBiggestCraftableStack(recipe, Integer.MAX_VALUE, output); } -- public int getBiggestCraftableStack(Recipe recipe, int maxCount, StackedContents.@Nullable Output> output) { -+ public int getBiggestCraftableStack(Recipe recipe, int maxCount, StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients - return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxCount, output); +- public int getBiggestCraftableStack(final Recipe recipe, final int maxSize, final StackedContents.@Nullable Output> output) { ++ public int getBiggestCraftableStack(final Recipe recipe, final int maxSize, final StackedContents.@Nullable Output output) { // Paper - Improve exact choice recipe ingredients + return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxSize, output); } diff --git a/net/minecraft/world/item/crafting/Ingredient.java b/net/minecraft/world/item/crafting/Ingredient.java -index ac4520afe510df9b1dd256d13dcb0768c83edb70..da95f40503f3696c8daa8fd0e947508fe43060ba 100644 +index 3a8a07712a15167dabd12d4bfd258de11706ce93..1deeb3fd148c5d59250346147681b7e8f2147574 100644 --- a/net/minecraft/world/item/crafting/Ingredient.java +++ b/net/minecraft/world/item/crafting/Ingredient.java -@@ -21,7 +21,7 @@ import net.minecraft.world.item.Items; +@@ -22,7 +22,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.display.SlotDisplay; import net.minecraft.world.level.ItemLike; --public final class Ingredient implements StackedContents.IngredientInfo>, Predicate { -+public final class Ingredient implements StackedContents.IngredientInfo, Predicate { // Paper - Improve exact choice recipe ingredients +-public final class Ingredient implements Predicate, StackedContents.IngredientInfo> { ++public final class Ingredient implements Predicate, StackedContents.IngredientInfo { // Paper - Improve exact choice recipe ingredients public static final StreamCodec CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) - .map(Ingredient::new, ingredient -> ingredient.values); + .map(Ingredient::new, i -> i.values); public static final StreamCodec> OPTIONAL_CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) -@@ -35,20 +35,24 @@ public final class Ingredient implements StackedContents.IngredientInfo, StackedContents.I + public static final Codec CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, i -> i.values); public final HolderSet values; // CraftBukkit start - @javax.annotation.Nullable -- private java.util.List itemStacks; -+ private java.util.Set itemStacks; // Paper - Improve exact choice recipe ingredients +- private java.util.@org.jspecify.annotations.Nullable List itemStacks; ++ private java.util.@org.jspecify.annotations.Nullable Set itemStacks; // Paper - Improve exact choice recipe ingredients public boolean isExact() { return this.itemStacks != null; } - @javax.annotation.Nullable -- public java.util.List itemStacks() { -+ public java.util.Set itemStacks() { // Paper - Improve exact choice recipe ingredients +- public java.util.@org.jspecify.annotations.Nullable List itemStacks() { ++ public java.util.@org.jspecify.annotations.Nullable Set itemStacks() { // Paper - Improve exact choice recipe ingredients return this.itemStacks; } public static Ingredient ofStacks(java.util.List stacks) { - Ingredient recipe = Ingredient.of(stacks.stream().map(ItemStack::getItem)); -- recipe.itemStacks = stacks; + Ingredient ingredient = Ingredient.of(stacks.stream().map(ItemStack::getItem)); +- ingredient.itemStacks = stacks; + // Paper start - Improve exact choice recipe ingredients -+ recipe.itemStacks = net.minecraft.world.item.ItemStackLinkedSet.createTypeAndComponentsSet(); -+ recipe.itemStacks.addAll(stacks); -+ recipe.itemStacks = java.util.Collections.unmodifiableSet(recipe.itemStacks); ++ ingredient.itemStacks = net.minecraft.world.item.ItemStackLinkedSet.createTypeAndComponentsSet(); ++ ingredient.itemStacks.addAll(stacks); ++ ingredient.itemStacks = java.util.Collections.unmodifiableSet(ingredient.itemStacks); + // Paper end - Improve exact choice recipe ingredients - return recipe; + return ingredient; } // CraftBukkit end -@@ -81,21 +85,22 @@ public final class Ingredient implements StackedContents.IngredientInfo, StackedContents.I + public boolean test(final ItemStack input) { // CraftBukkit start if (this.isExact()) { -- for (ItemStack itemstack1 : this.itemStacks()) { -- if (itemstack1.getItem() == stack.getItem() && ItemStack.isSameItemSameComponents(stack, itemstack1)) { +- for (ItemStack item : this.itemStacks()) { +- if (item.getItem() == input.getItem() && ItemStack.isSameItemSameComponents(input, item)) { - return true; - } - } - - return false; -+ return this.itemStacks.contains(stack); // Paper - Improve exact choice recipe ingredients (hashing FTW!) ++ return this.itemStacks.contains(input); // Paper - Improve exact choice recipe ingredients (hashing FTW!) } // CraftBukkit end - return stack.is(this.values); + return input.is(this.values); } + // Paper start - Improve exact choice recipe ingredients @Override -- public boolean acceptsItem(Holder item) { +- public boolean acceptsItem(final Holder item) { - return this.values.contains(item); + public boolean acceptsItem(final io.papermc.paper.inventory.recipe.ItemOrExact itemOrExact) { + return switch (itemOrExact) { @@ -410,26 +415,26 @@ index ac4520afe510df9b1dd256d13dcb0768c83edb70..da95f40503f3696c8daa8fd0e947508f } @Override -@@ -120,6 +125,11 @@ public final class Ingredient implements StackedContents.IngredientInfo, StackedContents.I } public SlotDisplay display() { + // Paper start - show exact ingredients in recipe book + if (this.isExact()) { -+ return new SlotDisplay.Composite(this.itemStacks().stream().map(SlotDisplay.ItemStackSlotDisplay::new).toList()); ++ return new SlotDisplay.Composite(this.itemStacks().stream().map(ItemStackTemplate::fromNonEmptyStack).map(SlotDisplay.ItemStackSlotDisplay::new).toList()); + } + // Paper end - show exact ingredients in recipe book return (SlotDisplay)this.values .unwrap() - .map(SlotDisplay.TagSlotDisplay::new, list -> new SlotDisplay.Composite(list.stream().map(Ingredient::displayForSingleItem).toList())); + .map(SlotDisplay.TagSlotDisplay::new, l -> new SlotDisplay.Composite(l.stream().map(Ingredient::displayForSingleItem).toList())); diff --git a/net/minecraft/world/item/crafting/ShapelessRecipe.java b/net/minecraft/world/item/crafting/ShapelessRecipe.java -index 43c8b7a027c519196852602d136a31f8c392b786..28d085fce206634ee88c492e06fdbf63a618ee12 100644 +index d4d4fad66be0f0c0225993026602dc61e10c6bce..53a818f14709d1978dbbc4aa7367be733a8ee47f 100644 --- a/net/minecraft/world/item/crafting/ShapelessRecipe.java +++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java -@@ -71,13 +71,18 @@ public class ShapelessRecipe implements CraftingRecipe { +@@ -74,13 +74,18 @@ public class ShapelessRecipe extends NormalCraftingRecipe { @Override - public boolean matches(CraftingInput input, Level level) { + public boolean matches(final CraftingInput input, final Level level) { + // Paper start - Improve exact choice recipe ingredients & unwrap ternary if (input.ingredientCount() != this.ingredients.size()) { return false;