--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java
+++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java
@@ -148,7 +_,7 @@
         return Leashable.createQuadLeashOffsets(this, -0.01, 0.63, 0.38, 1.15);
     }
 
-    private Sniffer.State getState() {
+    public Sniffer.State getState() {
         return this.entityData.get(DATA_STATE);
     }
 
@@ -244,7 +_,7 @@
         return this;
     }
 
-    Optional<BlockPos> calculateDigPosition() {
+    public Optional<BlockPos> calculateDigPosition() {
         return IntStream.range(0, 5)
             .mapToObj(i -> LandRandomPos.getPos(this, 10 + 2 * i, 3))
             .filter(Objects::nonNull)
@@ -255,7 +_,7 @@
             .findFirst();
     }
 
-    boolean canDig() {
+    public boolean canDig() {
         return !this.isPanicking()
             && !this.isTempted()
             && !this.isBaby()
@@ -277,6 +_,13 @@
             this.dropFromGiftLootTable(serverLevel, BuiltInLootTables.SNIFFER_DIGGING, (serverLevel1, itemStack) -> {
                 ItemEntity itemEntity = new ItemEntity(this.level(), headBlock.getX(), headBlock.getY(), headBlock.getZ(), itemStack);
                 itemEntity.setDefaultPickUpDelay();
+                // CraftBukkit start - handle EntityDropItemEvent
+                org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
+                org.bukkit.Bukkit.getPluginManager().callEvent(event);
+                if (event.isCancelled()) {
+                    return;
+                }
+                // CraftBukkit end
                 serverLevel1.addFreshEntity(itemEntity);
             });
             this.playSound(SoundEvents.SNIFFER_DROP_SEED, 1.0F, 1.0F);
@@ -310,14 +_,14 @@
         return this;
     }
 
-    private Sniffer storeExploredPosition(BlockPos pos) {
+    public Sniffer storeExploredPosition(BlockPos pos) {
         List<GlobalPos> list = this.getExploredPositions().limit(20L).collect(Collectors.toList());
         list.add(0, GlobalPos.of(this.level().dimension(), pos));
         this.getBrain().setMemory(MemoryModuleType.SNIFFER_EXPLORED_POSITIONS, list);
         return this;
     }
 
-    private Stream<GlobalPos> getExploredPositions() {
+    public Stream<GlobalPos> getExploredPositions() {
         return this.getBrain().getMemory(MemoryModuleType.SNIFFER_EXPLORED_POSITIONS).stream().flatMap(Collection::stream);
     }
 
@@ -335,12 +_,17 @@
 
     @Override
     public void spawnChildFromBreeding(ServerLevel level, Animal mate) {
+        // Paper start - Add EntityFertilizeEggEvent event
+        final io.papermc.paper.event.entity.EntityFertilizeEggEvent result = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityFertilizeEggEvent(this, mate);
+        if (result.isCancelled()) return;
+        // Paper end - Add EntityFertilizeEggEvent event
         ItemStack itemStack = new ItemStack(Items.SNIFFER_EGG);
         ItemEntity itemEntity = new ItemEntity(level, this.position().x(), this.position().y(), this.position().z(), itemStack);
         itemEntity.setDefaultPickUpDelay();
-        this.finalizeSpawnChildFromBreeding(level, mate, null);
+        this.finalizeSpawnChildFromBreeding(level, mate, null, result.getExperience()); // Paper - Add EntityFertilizeEggEvent event
+        if (this.spawnAtLocation(level, itemEntity) != null) { // Paper - Call EntityDropItemEvent
         this.playSound(SoundEvents.SNIFFER_EGG_PLOP, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 0.5F);
-        level.addFreshEntity(itemEntity);
+        } // Paper - Call EntityDropItemEvent
     }
 
     @Override
@@ -391,17 +_,17 @@
     }
 
     @Override
-    protected SoundEvent getAmbientSound() {
+    public SoundEvent getAmbientSound() {
         return Set.of(Sniffer.State.DIGGING, Sniffer.State.SEARCHING).contains(this.getState()) ? null : SoundEvents.SNIFFER_IDLE;
     }
 
     @Override
-    protected SoundEvent getHurtSound(DamageSource damageSource) {
+    public SoundEvent getHurtSound(DamageSource damageSource) {
         return SoundEvents.SNIFFER_HURT;
     }
 
     @Override
-    protected SoundEvent getDeathSound() {
+    public SoundEvent getDeathSound() {
         return SoundEvents.SNIFFER_DEATH;
     }
 
