diff --git a/patches/api/Expand-PlayerItemMendEvent.patch b/patches/api/Expand-PlayerItemMendEvent.patch
new file mode 100644
index 0000000000..4a4078dd84
--- /dev/null
+++ b/patches/api/Expand-PlayerItemMendEvent.patch
@@ -0,0 +1,71 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Thu, 20 Jan 2022 18:11:44 -0800
+Subject: [PATCH] Expand PlayerItemMendEvent
+
+
+diff --git a/src/main/java/org/bukkit/event/player/PlayerItemMendEvent.java b/src/main/java/org/bukkit/event/player/PlayerItemMendEvent.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/event/player/PlayerItemMendEvent.java
++++ b/src/main/java/org/bukkit/event/player/PlayerItemMendEvent.java
+@@ -0,0 +0,0 @@ public class PlayerItemMendEvent extends PlayerEvent implements Cancellable {
+     private final ExperienceOrb experienceOrb;
+     private int repairAmount;
+     private boolean cancelled;
++    private java.util.function.IntUnaryOperator durabilityToXpOp; // Paper
+ 
++    @Deprecated // Paper
+     public PlayerItemMendEvent(@NotNull Player who, @NotNull ItemStack item, @NotNull EquipmentSlot slot, @NotNull ExperienceOrb experienceOrb, int repairAmount) {
++        // Paper start
++        this(who, item, slot, experienceOrb, repairAmount, amount -> amount / 2);
++    }
++
++    @org.jetbrains.annotations.ApiStatus.Internal
++    public PlayerItemMendEvent(@NotNull Player who, @NotNull ItemStack item, @NotNull EquipmentSlot slot, @NotNull ExperienceOrb experienceOrb, int repairAmount, @NotNull java.util.function.IntUnaryOperator durabilityToXpOp) {
++        // Paper end
+         super(who);
+         this.item = item;
+         this.slot = slot;
+         this.experienceOrb = experienceOrb;
+         this.repairAmount = repairAmount;
++        // Paper start
++        this.durabilityToXpOp = durabilityToXpOp;
++    }
++
++    /**
++     * Get the operation used to calculate xp used based on
++     * the set repair amount. Used to calculate how much of
++     * an XP orb will be consumed by this mend operation.
++     *
++     * @return the durability-to-xp operation
++     */
++    public @NotNull java.util.function.IntUnaryOperator getDurabilityToXpOperation() {
++        return this.durabilityToXpOp;
++    }
++
++    /**
++     * Sets the operation used to calculate xp used based on
++     * the set repair amount. Used to calculate how much of
++     * an XP orb will be consumed by this mend operation.
++     *
++     * @param durabilityToXpOp the durability-to-xp operation
++     */
++    public void setDurabilityToXpOperation(@NotNull java.util.function.IntUnaryOperator durabilityToXpOp) {
++        com.google.common.base.Preconditions.checkNotNull(durabilityToXpOp);
++        this.durabilityToXpOp = durabilityToXpOp;
++    }
++
++    /**
++     * Helper method to get the amount of experience that will be consumed.
++     * This method just returns the result of inputting {@link #getRepairAmount()}
++     * into the function {@link #getDurabilityToXpOperation()}.
++     *
++     * @return the amount of xp that will be consumed
++     */
++    public int getConsumedExperience() {
++        return this.durabilityToXpOp.applyAsInt(this.getRepairAmount());
+     }
++    // Paper end
+ 
+     @Deprecated
+     public PlayerItemMendEvent(@NotNull Player who, @NotNull ItemStack item, @NotNull ExperienceOrb experienceOrb, int repairAmount) {
diff --git a/patches/server/Expand-PlayerItemMendEvent.patch b/patches/server/Expand-PlayerItemMendEvent.patch
new file mode 100644
index 0000000000..599b0aba5c
--- /dev/null
+++ b/patches/server/Expand-PlayerItemMendEvent.patch
@@ -0,0 +1,69 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Thu, 20 Jan 2022 18:11:20 -0800
+Subject: [PATCH] Expand PlayerItemMendEvent
+
+
+diff --git a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
++++ b/src/main/java/net/minecraft/world/entity/ExperienceOrb.java
+@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
+             ItemStack itemstack = (ItemStack) entry.getValue();
+             int j = Math.min(this.xpToDurability(this.value), itemstack.getDamageValue());
+             // CraftBukkit start
+-            org.bukkit.event.player.PlayerItemMendEvent event = CraftEventFactory.callPlayerItemMendEvent(player, this, itemstack, entry.getKey(), j);
++            org.bukkit.event.player.PlayerItemMendEvent event = CraftEventFactory.callPlayerItemMendEvent(player, this, itemstack, entry.getKey(), j, this::durabilityToXp); // Paper
+             j = event.getRepairAmount();
+             if (event.isCancelled()) {
+                 return amount;
+@@ -0,0 +0,0 @@ public class ExperienceOrb extends Entity {
+             // CraftBukkit end
+ 
+             itemstack.setDamageValue(itemstack.getDamageValue() - j);
+-            int k = amount - this.durabilityToXp(j);
++            int k = amount - event.getDurabilityToXpOperation().applyAsInt(j); // Paper
+             this.value = k; // CraftBukkit - update exp value of orb for PlayerItemMendEvent calls
++            // Paper start
++            if (j == 0 && amount == k) { // if repair amount is 0 and no xp was removed, don't do recursion; treat as cancelled
++                return k;
++            }
++            // Paper end
+ 
+             return k > 0 ? this.repairPlayerItems(player, k) : 0;
+         } else {
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+             orb.setPosRaw(handle.getX(), handle.getY(), handle.getZ());
+ 
+             int i = Math.min(orb.xpToDurability(amount), itemstack.getDamageValue());
+-            org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(handle, orb, itemstack, stackEntry.getKey(), i);
++            org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(handle, orb, itemstack, stackEntry.getKey(), i, orb::durabilityToXp); // Paper
+             i = event.getRepairAmount();
+             orb.discard();
+             if (!event.isCancelled()) {
+-                amount -= orb.durabilityToXp(i);
++                amount -= event.getDurabilityToXpOperation().applyAsInt(i); // Paper
+                 itemstack.setDamageValue(itemstack.getDamageValue() - i);
+             }
+         }
+diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -0,0 +0,0 @@ public class CraftEventFactory {
+         return event;
+     }
+ 
+-    public static PlayerItemMendEvent callPlayerItemMendEvent(net.minecraft.world.entity.player.Player entity, net.minecraft.world.entity.ExperienceOrb orb, net.minecraft.world.item.ItemStack nmsMendedItem, net.minecraft.world.entity.EquipmentSlot slot, int repairAmount) {
++    public static PlayerItemMendEvent callPlayerItemMendEvent(net.minecraft.world.entity.player.Player entity, net.minecraft.world.entity.ExperienceOrb orb, net.minecraft.world.item.ItemStack nmsMendedItem, net.minecraft.world.entity.EquipmentSlot slot, int repairAmount, java.util.function.IntUnaryOperator durabilityToXpOp) { // Paper
+         Player player = (Player) entity.getBukkitEntity();
+         org.bukkit.inventory.ItemStack bukkitStack = CraftItemStack.asCraftMirror(nmsMendedItem);
+-        PlayerItemMendEvent event = new PlayerItemMendEvent(player, bukkitStack, CraftEquipmentSlot.getSlot(slot), (ExperienceOrb) orb.getBukkitEntity(), repairAmount);
++        PlayerItemMendEvent event = new PlayerItemMendEvent(player, bukkitStack, CraftEquipmentSlot.getSlot(slot), (ExperienceOrb) orb.getBukkitEntity(), repairAmount, durabilityToXpOp); // Paper
+         Bukkit.getPluginManager().callEvent(event);
+         return event;
+     }