--- a/net/minecraft/world/entity/player/StackedItemContents.java
+++ b/net/minecraft/world/entity/player/StackedItemContents.java
@@ -9,9 +_,14 @@
 import net.minecraft.world.item.crafting.Recipe;
 
 public class StackedItemContents {
-    private final StackedContents<Holder<Item>> raw = new StackedContents<>();
+    // Paper start - Improve exact choice recipe ingredients
+    private final StackedContents<io.papermc.paper.inventory.recipe.ItemOrExact> raw = new StackedContents<>();
+    @Nullable
+    private io.papermc.paper.inventory.recipe.StackedContentsExtrasMap extrasMap;
+    // Paper start - 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);
         }
@@ -24,34 +_,51 @@
     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);
         }
     }
 
-    public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> output) {
+    public boolean canCraft(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
         return this.canCraft(recipe, 1, output);
     }
 
-    public boolean canCraft(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output) {
+    // Paper start - Improve exact choice recipe ingredients
+    public void initializeExtras(final Recipe<?> recipe, @Nullable final net.minecraft.world.item.crafting.CraftingInput input) {
+        if (this.extrasMap == null) {
+            this.extrasMap = new io.papermc.paper.inventory.recipe.StackedContentsExtrasMap(this.raw);
+        }
+        this.extrasMap.initialize(recipe);
+        if (input != null) this.extrasMap.accountInput(input);
+    }
+
+    public void resetExtras() {
+        if (this.extrasMap != null && !this.raw.amounts.isEmpty()) {
+            this.extrasMap.resetExtras();
+        }
+    }
+    // Paper end - Improve exact choice recipe ingredients
+
+    public boolean canCraft(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
         PlacementInfo placementInfo = recipe.placementInfo();
         return !placementInfo.isImpossibleToPlace() && this.canCraft(placementInfo.ingredients(), maxCount, output);
     }
 
-    public boolean canCraft(List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, @Nullable StackedContents.Output<Holder<Item>> output) {
+    public boolean canCraft(List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
         return this.canCraft(ingredients, 1, output);
     }
 
     private boolean canCraft(
-        List<? extends StackedContents.IngredientInfo<Holder<Item>>> ingredients, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output
+        List<? extends StackedContents.IngredientInfo<io.papermc.paper.inventory.recipe.ItemOrExact>> ingredients, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output // Paper - Improve exact choice recipe ingredients
     ) {
         return this.raw.tryPick(ingredients, maxCount, output);
     }
 
-    public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<Holder<Item>> output) {
+    public int getBiggestCraftableStack(Recipe<?> recipe, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
         return this.getBiggestCraftableStack(recipe, Integer.MAX_VALUE, output);
     }
 
-    public int getBiggestCraftableStack(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<Holder<Item>> output) {
+    public int getBiggestCraftableStack(Recipe<?> recipe, int maxCount, @Nullable StackedContents.Output<io.papermc.paper.inventory.recipe.ItemOrExact> output) { // Paper - Improve exact choice recipe ingredients
         return this.raw.tryPickAll(recipe.placementInfo().ingredients(), maxCount, output);
     }
 
