--- a/net/minecraft/world/entity/animal/sheep/Sheep.java
+++ b/net/minecraft/world/entity/animal/sheep/Sheep.java
@@ -139,7 +_,19 @@
         ItemStack itemInHand = player.getItemInHand(hand);
         if (itemInHand.is(Items.SHEARS)) {
             if (this.level() instanceof ServerLevel serverLevel && this.readyForShearing()) {
-                this.shear(serverLevel, SoundSource.PLAYERS, itemInHand);
+                // CraftBukkit start
+                // Paper start - custom shear drops
+                java.util.List<ItemStack> drops = this.generateDefaultDrops(serverLevel, itemInHand);
+                org.bukkit.event.player.PlayerShearEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemInHand, hand, drops);
+                if (event != null) {
+                    if (event.isCancelled()) {
+                        return InteractionResult.PASS;
+                    }
+                    drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
+                    // Paper end - custom shear drops
+                }
+                // CraftBukkit end
+                this.shear(serverLevel, SoundSource.PLAYERS, itemInHand, drops); // Paper - custom shear drops
                 this.gameEvent(GameEvent.SHEAR, player);
                 itemInHand.hurtAndBreak(1, player, getSlotForHand(hand));
                 return InteractionResult.SUCCESS_SERVER;
@@ -153,14 +_,28 @@
 
     @Override
     public void shear(ServerLevel level, SoundSource soundSource, ItemStack shears) {
+        // Paper start - custom shear drops
+        this.shear(level, soundSource, shears, this.generateDefaultDrops(level, shears));
+    }
+
+    @Override
+    public java.util.List<ItemStack> generateDefaultDrops(final ServerLevel serverLevel, final ItemStack shears) {
+        final java.util.List<ItemStack> drops = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
+        this.dropFromShearingLootTable(serverLevel, BuiltInLootTables.SHEAR_SHEEP, shears, (ignored, stack) -> {
+            for (int i = 0; i < stack.getCount(); ++i) drops.add(stack.copyWithCount(1));
+        });
+        return drops;
+    }
+
+    @Override
+    public void shear(ServerLevel level, SoundSource soundSource, ItemStack shears, java.util.List<ItemStack> drops) {
+        // Paper end - custom shear drops
         level.playSound(null, this, SoundEvents.SHEEP_SHEAR, soundSource, 1.0F, 1.0F);
-        this.dropFromShearingLootTable(
-            level,
-            BuiltInLootTables.SHEAR_SHEEP,
-            shears,
-            (serverLevel, itemStack) -> {
-                for (int i = 0; i < itemStack.getCount(); i++) {
-                    ItemEntity itemEntity = this.spawnAtLocation(serverLevel, itemStack.copyWithCount(1), 1.0F);
+        drops.forEach(itemStack -> { // Paper - custom drops - loop in generated default drops
+            if (true) { // Paper - custom drops - loop in generated default drops
+                this.forceDrops = true; // CraftBukkit
+                ItemEntity itemEntity = this.spawnAtLocation(level, itemStack, 1.0F); // Paper - custom drops - copy already done above
+                this.forceDrops = false; // CraftBukkit
                     if (itemEntity != null) {
                         itemEntity.setDeltaMovement(
                             itemEntity.getDeltaMovement()
@@ -280,6 +_,7 @@
 
     @Override
     public void ate() {
+        if (!new org.bukkit.event.entity.SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()).callEvent()) return; // CraftBukkit
         super.ate();
         this.setSheared(false);
         if (this.isBaby()) {
