--- a/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -4,6 +4,7 @@
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParseException;
 import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.Iterator;
 import net.minecraft.core.NonNullList;
 import net.minecraft.network.FriendlyByteBuf;
 import net.minecraft.resources.ResourceLocation;
@@ -12,8 +13,14 @@
 import net.minecraft.world.inventory.CraftingContainer;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.level.Level;
+// CraftBukkit start
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftRecipe;
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftShapelessRecipe;
+// CraftBukkit end
 
 public class ShapelessRecipe implements CraftingRecipe {
+
     private final ResourceLocation id;
     final String group;
     final ItemStack result;
@@ -26,6 +33,20 @@
         this.ingredients = input;
     }
 
+    // CraftBukkit start
+    @SuppressWarnings("unchecked")
+    public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe() {
+        CraftItemStack result = CraftItemStack.asCraftMirror(this.result);
+        CraftShapelessRecipe recipe = new CraftShapelessRecipe(result, this);
+        recipe.setGroup(this.group);
+
+        for (Ingredient list : this.ingredients) {
+            recipe.addIngredient(CraftRecipe.toBukkit(list));
+        }
+        return recipe;
+    }
+    // CraftBukkit end
+
     @Override
     public ResourceLocation getId() {
         return this.id;
@@ -51,23 +72,55 @@
         return this.ingredients;
     }
 
-    @Override
     public boolean matches(CraftingContainer inventory, Level world) {
-        StackedContents stackedContents = new StackedContents();
+        StackedContents autorecipestackmanager = new StackedContents();
         int i = 0;
 
-        for(int j = 0; j < inventory.getContainerSize(); ++j) {
-            ItemStack itemStack = inventory.getItem(j);
-            if (!itemStack.isEmpty()) {
-                ++i;
-                stackedContents.accountStack(itemStack, 1);
+        // Paper start
+        java.util.List<ItemStack> providedItems = new java.util.ArrayList<>();
+        co.aikar.util.Counter<ItemStack> matchedProvided = new co.aikar.util.Counter<>();
+        co.aikar.util.Counter<Ingredient> matchedIngredients = new co.aikar.util.Counter<>();
+        // Paper end
+        for (int j = 0; j < inventory.getContainerSize(); ++j) {
+            ItemStack itemstack = inventory.getItem(j);
+
+            if (!itemstack.isEmpty()) {
+                // Paper start
+                itemstack = itemstack.copy();
+                providedItems.add(itemstack);
+                for (Ingredient ingredient : ingredients) {
+                    if (ingredient.test(itemstack)) {
+                        matchedProvided.increment(itemstack);
+                        matchedIngredients.increment(ingredient);
+                    }
+                }
+                // Paper end
             }
         }
 
-        return i == this.ingredients.size() && stackedContents.canCraft(this, (IntList)null);
+        // Paper start
+        if (matchedProvided.isEmpty() || matchedIngredients.isEmpty()) {
+            return false;
+        }
+        java.util.List<Ingredient> ingredients = new java.util.ArrayList<>(this.ingredients);
+        providedItems.sort(java.util.Comparator.comparingInt((ItemStack c) -> (int) matchedProvided.getCount(c)).reversed());
+        ingredients.sort(java.util.Comparator.comparingInt((Ingredient c) -> (int) matchedIngredients.getCount(c)));
+
+        PROVIDED:
+        for (ItemStack provided : providedItems) {
+            for (Iterator<Ingredient> itIngredient = ingredients.iterator(); itIngredient.hasNext(); ) {
+                Ingredient ingredient = itIngredient.next();
+                if (ingredient.test(provided)) {
+                    itIngredient.remove();
+                    continue PROVIDED;
+                }
+            }
+            return false;
+        }
+        return ingredients.isEmpty();
+        // Paper end
     }
 
-    @Override
     public ItemStack assemble(CraftingContainer inventory) {
         return this.result.copy();
     }
@@ -78,54 +131,63 @@
     }
 
     public static class Serializer implements RecipeSerializer<ShapelessRecipe> {
+
+        public Serializer() {}
+
         @Override
-        public ShapelessRecipe fromJson(ResourceLocation resourceLocation, JsonObject jsonObject) {
-            String string = GsonHelper.getAsString(jsonObject, "group", "");
-            NonNullList<Ingredient> nonNullList = itemsFromJson(GsonHelper.getAsJsonArray(jsonObject, "ingredients"));
-            if (nonNullList.isEmpty()) {
+        public ShapelessRecipe fromJson(ResourceLocation id, JsonObject json) {
+            String s = GsonHelper.getAsString(json, "group", "");
+            NonNullList<Ingredient> nonnulllist = Serializer.itemsFromJson(GsonHelper.getAsJsonArray(json, "ingredients"));
+
+            if (nonnulllist.isEmpty()) {
                 throw new JsonParseException("No ingredients for shapeless recipe");
-            } else if (nonNullList.size() > 9) {
+            } else if (nonnulllist.size() > 9) {
                 throw new JsonParseException("Too many ingredients for shapeless recipe");
             } else {
-                ItemStack itemStack = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(jsonObject, "result"));
-                return new ShapelessRecipe(resourceLocation, string, itemStack, nonNullList);
+                ItemStack itemstack = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
+
+                return new ShapelessRecipe(id, s, itemstack, nonnulllist);
             }
         }
 
         private static NonNullList<Ingredient> itemsFromJson(JsonArray json) {
-            NonNullList<Ingredient> nonNullList = NonNullList.create();
+            NonNullList<Ingredient> nonnulllist = NonNullList.create();
 
-            for(int i = 0; i < json.size(); ++i) {
-                Ingredient ingredient = Ingredient.fromJson(json.get(i));
-                if (!ingredient.isEmpty()) {
-                    nonNullList.add(ingredient);
+            for (int i = 0; i < json.size(); ++i) {
+                Ingredient recipeitemstack = Ingredient.fromJson(json.get(i));
+
+                if (!recipeitemstack.isEmpty()) {
+                    nonnulllist.add(recipeitemstack);
                 }
             }
 
-            return nonNullList;
+            return nonnulllist;
         }
 
         @Override
-        public ShapelessRecipe fromNetwork(ResourceLocation resourceLocation, FriendlyByteBuf friendlyByteBuf) {
-            String string = friendlyByteBuf.readUtf();
-            int i = friendlyByteBuf.readVarInt();
-            NonNullList<Ingredient> nonNullList = NonNullList.withSize(i, Ingredient.EMPTY);
+        public ShapelessRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buf) {
+            String s = buf.readUtf();
+            int i = buf.readVarInt();
+            NonNullList<Ingredient> nonnulllist = NonNullList.withSize(i, Ingredient.EMPTY);
 
-            for(int j = 0; j < nonNullList.size(); ++j) {
-                nonNullList.set(j, Ingredient.fromNetwork(friendlyByteBuf));
+            for (int j = 0; j < nonnulllist.size(); ++j) {
+                nonnulllist.set(j, Ingredient.fromNetwork(buf));
             }
 
-            ItemStack itemStack = friendlyByteBuf.readItem();
-            return new ShapelessRecipe(resourceLocation, string, itemStack, nonNullList);
+            ItemStack itemstack = buf.readItem();
+
+            return new ShapelessRecipe(id, s, itemstack, nonnulllist);
         }
 
-        @Override
         public void toNetwork(FriendlyByteBuf buf, ShapelessRecipe recipe) {
             buf.writeUtf(recipe.group);
             buf.writeVarInt(recipe.ingredients.size());
+            Iterator iterator = recipe.ingredients.iterator();
 
-            for(Ingredient ingredient : recipe.ingredients) {
-                ingredient.toNetwork(buf);
+            while (iterator.hasNext()) {
+                Ingredient recipeitemstack = (Ingredient) iterator.next();
+
+                recipeitemstack.toNetwork(buf);
             }
 
             buf.writeItem(recipe.result);
