--- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
@@ -25,32 +25,70 @@
 import net.minecraft.world.level.block.state.BlockState;
 import net.minecraft.world.level.gameevent.GameEvent;
 
+// CraftBukkit start
+import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockCookEvent;
+// CraftBukkit end
+
 public class CampfireBlockEntity extends BlockEntity implements Clearable {
+
     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);
-    public final int[] cookingProgress = new int[4];
-    public final int[] cookingTime = new int[4];
-    private final RecipeManager.CachedCheck<Container, CampfireCookingRecipe> quickCheck = RecipeManager.createCheck(RecipeType.CAMPFIRE_COOKING);
+    private final NonNullList<ItemStack> items;
+    public final int[] cookingProgress;
+    public final int[] cookingTime;
+    private final RecipeManager.CachedCheck<Container, CampfireCookingRecipe> quickCheck;
+    public final boolean[] stopCooking; // Paper
 
     public CampfireBlockEntity(BlockPos pos, BlockState state) {
         super(BlockEntityType.CAMPFIRE, pos, state);
+        this.items = NonNullList.withSize(4, ItemStack.EMPTY);
+        this.cookingProgress = new int[4];
+        this.cookingTime = new int[4];
+        this.quickCheck = RecipeManager.createCheck(RecipeType.CAMPFIRE_COOKING);
+        this.stopCooking = new boolean[4]; // Paper
     }
 
     public static void cookTick(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire) {
-        boolean bl = false;
+        boolean flag = false;
+
+        for (int i = 0; i < campfire.items.size(); ++i) {
+            ItemStack itemstack = (ItemStack) campfire.items.get(i);
+
+            if (!itemstack.isEmpty()) {
+                flag = true;
+                if (!campfire.stopCooking[i]) { // Paper
+                int j = campfire.cookingProgress[i]++;
+                } // Paper
 
-        for(int i = 0; i < campfire.items.size(); ++i) {
-            ItemStack itemStack = campfire.items.get(i);
-            if (!itemStack.isEmpty()) {
-                bl = true;
-                int var10002 = campfire.cookingProgress[i]++;
                 if (campfire.cookingProgress[i] >= campfire.cookingTime[i]) {
-                    Container container = new SimpleContainer(itemStack);
-                    ItemStack itemStack2 = campfire.quickCheck.getRecipeFor(container, world).map((recipe) -> {
-                        return recipe.assemble(container);
-                    }).orElse(itemStack);
-                    Containers.dropItemStack(world, (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), itemStack2);
+                    SimpleContainer inventorysubcontainer = new SimpleContainer(new ItemStack[]{itemstack});
+                    Optional<CampfireCookingRecipe> recipe = campfire.quickCheck.getRecipeFor( inventorysubcontainer, world);
+                    ItemStack itemstack1 = (ItemStack) recipe.map((recipecampfire) -> {
+                        // Paper end
+                        return recipecampfire.assemble(inventorysubcontainer);
+                    }).orElse(itemstack);
+
+                    // CraftBukkit start - fire BlockCookEvent
+                    CraftItemStack source = CraftItemStack.asCraftMirror(itemstack);
+                    org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1);
+
+                    BlockCookEvent blockCookEvent = new BlockCookEvent(CraftBlock.at(world, pos), source, result, (org.bukkit.inventory.CookingRecipe<?>) recipe.map(CampfireCookingRecipe::toBukkitRecipe).orElse(null)); // Paper
+                    world.getCraftServer().getPluginManager().callEvent(blockCookEvent);
+
+                    if (blockCookEvent.isCancelled()) {
+                        return;
+                    }
+
+                    result = blockCookEvent.getResult();
+                    itemstack1 = CraftItemStack.asNMSCopy(result);
+                    // CraftBukkit end
+                    // Paper start
+                    net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(world, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, itemstack1.split(world.random.nextInt(21) + 10));
+                    droppedItem.setDeltaMovement(world.random.nextGaussian() * 0.05D, world.random.nextGaussian() * 0.05D + 0.2D, world.random.nextGaussian() * 0.05D);
+                    world.addFreshEntity(droppedItem);
+                    // Paper end
                     campfire.items.set(i, ItemStack.EMPTY);
                     world.sendBlockUpdated(pos, state, state, 3);
                     world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state));
@@ -58,48 +96,50 @@
             }
         }
 
-        if (bl) {
+        if (flag) {
             setChanged(world, pos, state);
         }
 
     }
 
     public static void cooldownTick(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire) {
-        boolean bl = false;
+        boolean flag = false;
 
-        for(int i = 0; i < campfire.items.size(); ++i) {
+        for (int i = 0; i < campfire.items.size(); ++i) {
             if (campfire.cookingProgress[i] > 0) {
-                bl = true;
-                campfire.cookingProgress[i] = Mth.clamp(campfire.cookingProgress[i] - 2, 0, campfire.cookingTime[i]);
+                flag = true;
+                campfire.cookingProgress[i] = Mth.clamp(campfire.cookingProgress[i] - 2, (int) 0, campfire.cookingTime[i]);
             }
         }
 
-        if (bl) {
+        if (flag) {
             setChanged(world, pos, state);
         }
 
     }
 
     public static void particleTick(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire) {
-        RandomSource randomSource = world.random;
-        if (randomSource.nextFloat() < 0.11F) {
-            for(int i = 0; i < randomSource.nextInt(2) + 2; ++i) {
-                CampfireBlock.makeParticles(world, pos, state.getValue(CampfireBlock.SIGNAL_FIRE), false);
+        RandomSource randomsource = world.random;
+        int i;
+
+        if (randomsource.nextFloat() < 0.11F) {
+            for (i = 0; i < randomsource.nextInt(2) + 2; ++i) {
+                CampfireBlock.makeParticles(world, pos, (Boolean) state.getValue(CampfireBlock.SIGNAL_FIRE), false);
             }
         }
 
-        int j = state.getValue(CampfireBlock.FACING).get2DDataValue();
+        i = ((Direction) state.getValue(CampfireBlock.FACING)).get2DDataValue();
 
-        for(int k = 0; k < campfire.items.size(); ++k) {
-            if (!campfire.items.get(k).isEmpty() && randomSource.nextFloat() < 0.2F) {
-                Direction direction = Direction.from2DDataValue(Math.floorMod(k + j, 4));
+        for (int j = 0; j < campfire.items.size(); ++j) {
+            if (!((ItemStack) campfire.items.get(j)).isEmpty() && randomsource.nextFloat() < 0.2F) {
+                Direction enumdirection = Direction.from2DDataValue(Math.floorMod(j + i, 4));
                 float f = 0.3125F;
-                double d = (double)pos.getX() + 0.5D - (double)((float)direction.getStepX() * 0.3125F) + (double)((float)direction.getClockWise().getStepX() * 0.3125F);
-                double e = (double)pos.getY() + 0.5D;
-                double g = (double)pos.getZ() + 0.5D - (double)((float)direction.getStepZ() * 0.3125F) + (double)((float)direction.getClockWise().getStepZ() * 0.3125F);
+                double d0 = (double) pos.getX() + 0.5D - (double) ((float) enumdirection.getStepX() * 0.3125F) + (double) ((float) enumdirection.getClockWise().getStepX() * 0.3125F);
+                double d1 = (double) pos.getY() + 0.5D;
+                double d2 = (double) pos.getZ() + 0.5D - (double) ((float) enumdirection.getStepZ() * 0.3125F) + (double) ((float) enumdirection.getClockWise().getStepZ() * 0.3125F);
 
-                for(int l = 0; l < 4; ++l) {
-                    world.addParticle(ParticleTypes.SMOKE, d, e, g, 0.0D, 5.0E-4D, 0.0D);
+                for (int k = 0; k < 4; ++k) {
+                    world.addParticle(ParticleTypes.SMOKE, d0, d1, d2, 0.0D, 5.0E-4D, 0.0D);
                 }
             }
         }
@@ -115,16 +155,28 @@
         super.load(nbt);
         this.items.clear();
         ContainerHelper.loadAllItems(nbt, this.items);
+        int[] aint;
+
         if (nbt.contains("CookingTimes", 11)) {
-            int[] is = nbt.getIntArray("CookingTimes");
-            System.arraycopy(is, 0, this.cookingProgress, 0, Math.min(this.cookingTime.length, is.length));
+            aint = nbt.getIntArray("CookingTimes");
+            System.arraycopy(aint, 0, this.cookingProgress, 0, Math.min(this.cookingTime.length, aint.length));
         }
 
         if (nbt.contains("CookingTotalTimes", 11)) {
-            int[] js = nbt.getIntArray("CookingTotalTimes");
-            System.arraycopy(js, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, js.length));
+            aint = nbt.getIntArray("CookingTotalTimes");
+            System.arraycopy(aint, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, aint.length));
         }
 
+        // Paper start
+        if (nbt.contains("Paper.StopCooking", org.bukkit.craftbukkit.v1_19_R1.util.CraftMagicNumbers.NBT.TAG_BYTE_ARRAY)) {
+            byte[] abyte = nbt.getByteArray("Paper.StopCooking");
+            boolean[] cookingState = new boolean[4];
+            for (int index = 0; index < abyte.length; index++) {
+                cookingState[index] = abyte[index] == 1;
+            }
+            System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length));
+        }
+        // Paper end
     }
 
     @Override
@@ -133,6 +185,13 @@
         ContainerHelper.saveAllItems(nbt, this.items, true);
         nbt.putIntArray("CookingTimes", this.cookingProgress);
         nbt.putIntArray("CookingTotalTimes", this.cookingTime);
+        // Paper start
+        byte[] cookingState = new byte[4];
+        for (int index = 0; index < cookingState.length; index++) {
+            cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0);
+        }
+        nbt.putByteArray("Paper.StopCooking", cookingState);
+        // Paper end
     }
 
     @Override
@@ -142,22 +201,24 @@
 
     @Override
     public CompoundTag getUpdateTag() {
-        CompoundTag compoundTag = new CompoundTag();
-        ContainerHelper.saveAllItems(compoundTag, this.items, true);
-        return compoundTag;
+        CompoundTag nbttagcompound = new CompoundTag();
+
+        ContainerHelper.saveAllItems(nbttagcompound, this.items, true);
+        return nbttagcompound;
     }
 
     public Optional<CampfireCookingRecipe> getCookableRecipe(ItemStack stack) {
-        return this.items.stream().noneMatch(ItemStack::isEmpty) ? Optional.empty() : this.quickCheck.getRecipeFor(new SimpleContainer(stack), this.level);
+        return this.items.stream().noneMatch(ItemStack::isEmpty) ? Optional.empty() : this.quickCheck.getRecipeFor(new SimpleContainer(new ItemStack[]{stack}), this.level);
     }
 
     public boolean placeFood(@Nullable Entity user, ItemStack stack, int cookTime) {
-        for(int i = 0; i < this.items.size(); ++i) {
-            ItemStack itemStack = this.items.get(i);
-            if (itemStack.isEmpty()) {
-                this.cookingTime[i] = cookTime;
-                this.cookingProgress[i] = 0;
-                this.items.set(i, stack.split(1));
+        for (int j = 0; j < this.items.size(); ++j) {
+            ItemStack itemstack1 = (ItemStack) this.items.get(j);
+
+            if (itemstack1.isEmpty()) {
+                this.cookingTime[j] = cookTime;
+                this.cookingProgress[j] = 0;
+                this.items.set(j, stack.split(1));
                 this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(user, this.getBlockState()));
                 this.markUpdated();
                 return true;
