--- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
@@ -43,8 +_,9 @@
     private static final int BURN_COOL_SPEED = 2;
     private static final int NUM_SLOTS = 4;
     private final NonNullList<ItemStack> items = NonNullList.withSize(4, ItemStack.EMPTY);
-    private final int[] cookingProgress = new int[4];
-    private final int[] cookingTime = new int[4];
+    public final int[] cookingProgress = new int[4];
+    public final int[] cookingTime = new int[4];
+    public final boolean[] stopCooking = new boolean[4]; // Paper - Add more Campfire API
 
     public CampfireBlockEntity(BlockPos pos, BlockState blockState) {
         super(BlockEntityType.CAMPFIRE, pos, blockState);
@@ -63,14 +_,44 @@
             ItemStack itemStack = campfire.items.get(i);
             if (!itemStack.isEmpty()) {
                 flag = true;
+                if (!campfire.stopCooking[i]) { // Paper - Add more Campfire API
                 campfire.cookingProgress[i]++;
+                } // Paper - Add more Campfire API
                 if (campfire.cookingProgress[i] >= campfire.cookingTime[i]) {
                     SingleRecipeInput singleRecipeInput = new SingleRecipeInput(itemStack);
-                    ItemStack itemStack1 = check.getRecipeFor(singleRecipeInput, level)
+                    // Paper start - add recipe to cook events
+                    final var optionalCookingRecipe = check.getRecipeFor(singleRecipeInput, level);
+                    ItemStack itemStack1 = optionalCookingRecipe
                         .map(recipe -> recipe.value().assemble(singleRecipeInput, level.registryAccess()))
                         .orElse(itemStack);
+                    // Paper end - add recipe to cook events
                     if (itemStack1.isItemEnabled(level.enabledFeatures())) {
-                        Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack1);
+                        // CraftBukkit start - fire BlockCookEvent
+                        org.bukkit.craftbukkit.inventory.CraftItemStack source = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack);
+                        org.bukkit.inventory.ItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemStack1);
+
+                        org.bukkit.event.block.BlockCookEvent blockCookEvent = new org.bukkit.event.block.BlockCookEvent(
+                            org.bukkit.craftbukkit.block.CraftBlock.at(level, pos),
+                            source,
+                            result,
+                            (org.bukkit.inventory.CookingRecipe<?>) optionalCookingRecipe.map(RecipeHolder::toBukkitRecipe).orElse(null) // Paper -Add recipe to cook events
+                        );
+
+                        if (!blockCookEvent.callEvent()) {
+                            return;
+                        }
+
+                        result = blockCookEvent.getResult();
+                        itemStack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(result);
+                        // CraftBukkit end
+                        // Paper start - Fix item locations dropped from campfires
+                        double deviation = 0.05F * RandomSource.GAUSSIAN_SPREAD_FACTOR;
+                        while (!itemStack1.isEmpty()) {
+                            net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(level, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, itemStack1.split(level.random.nextInt(21) + 10));
+                            droppedItem.setDeltaMovement(level.random.triangle(0.0D, deviation), level.random.triangle(0.2D, deviation), level.random.triangle(0.0D, deviation));
+                            level.addFreshEntity(droppedItem);
+                        }
+                        // Paper end - Fix item locations dropped from campfires
                         campfire.items.set(i, ItemStack.EMPTY);
                         level.sendBlockUpdated(pos, state, state, 3);
                         level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state));
@@ -142,6 +_,16 @@
             .ifPresentOrElse(
                 ints -> System.arraycopy(ints, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, ints.length)), () -> Arrays.fill(this.cookingTime, 0)
             );
+
+        // Paper start - Add more Campfire API
+        input.read("Paper.StopCooking", com.mojang.serialization.Codec.BYTE_BUFFER).ifPresent(bytes -> {
+            final boolean[] cookingState = new boolean[4];
+            for (int index = 0; bytes.hasRemaining() && index < cookingState.length; index++) {
+                cookingState[index] = bytes.get() == 1;
+            }
+            System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, bytes.capacity()));
+        });
+        // Paper end - Add more Campfire API
     }
 
     @Override
@@ -150,6 +_,13 @@
         ContainerHelper.saveAllItems(output, this.items, true);
         output.putIntArray("CookingTimes", this.cookingProgress);
         output.putIntArray("CookingTotalTimes", this.cookingTime);
+        // Paper start - Add more Campfire API
+        byte[] cookingState = new byte[4];
+        for (int index = 0; index < cookingState.length; index++) {
+            cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0);
+        }
+        output.store("Paper.StopCooking", com.mojang.serialization.Codec.BYTE_BUFFER, java.nio.ByteBuffer.wrap(cookingState));
+        // Paper end - Add more Campfire API
     }
 
     @Override
@@ -179,7 +_,15 @@
                     return false;
                 }
 
-                this.cookingTime[i] = recipeFor.get().value().cookingTime();
+                // CraftBukkit start
+                org.bukkit.event.block.CampfireStartEvent event = new org.bukkit.event.block.CampfireStartEvent(
+                    org.bukkit.craftbukkit.block.CraftBlock.at(this.level,this.worldPosition),
+                    org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack),
+                    (org.bukkit.inventory.CampfireRecipe) recipeFor.get().toBukkitRecipe()
+                );
+                this.level.getCraftServer().getPluginManager().callEvent(event);
+                this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime()
+                // CraftBukkit end
                 this.cookingProgress[i] = 0;
                 this.items.set(i, stack.consumeAndReturn(1, entity));
                 level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState()));
