--- a/net/minecraft/server/BlockFire.java
+++ b/net/minecraft/server/BlockFire.java
@@ -8,6 +8,16 @@
 import java.util.function.Predicate;
 import javax.annotation.Nullable;
 
+// CraftBukkit start
+
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.block.CraftBlockState;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.block.BlockBurnEvent;
+import org.bukkit.event.block.BlockFadeEvent;
+import org.bukkit.event.block.BlockSpreadEvent;
+// CraftBukkit end
+
 public class BlockFire extends Block {
 
     public static final BlockStateInteger AGE = BlockProperties.X;
@@ -32,7 +42,20 @@
     }
 
     public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
-        return this.canPlace(iblockdata, generatoraccess, blockposition) ? (IBlockData) this.a((IBlockAccess) generatoraccess, blockposition).set(BlockFire.AGE, iblockdata.get(BlockFire.AGE)) : Blocks.AIR.getBlockData();
+        // CraftBukkit start
+        if (!iblockdata.canPlace(generatoraccess, blockposition)) {
+            CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
+            blockState.setData(Blocks.AIR.getBlockData());
+
+            BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState);
+            generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
+
+            if (!event.isCancelled()) {
+                return blockState.getHandle();
+            }
+        }
+        return this.a((IBlockAccess) generatoraccess, blockposition).set(BlockFire.AGE, iblockdata.get(BlockFire.AGE));
+        // CraftBukkit end
     }
 
     @Nullable
@@ -82,7 +105,7 @@
     public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
         if (world.getGameRules().getBoolean("doFireTick")) {
             if (!iblockdata.canPlace(world, blockposition)) {
-                world.setAir(blockposition);
+                fireExtinguished(world, blockposition); // CraftBukkit - invalid place location
             }
 
             Block block = world.getType(blockposition.down()).getBlock();
@@ -90,7 +113,7 @@
             int i = ((Integer) iblockdata.get(BlockFire.AGE)).intValue();
 
             if (!flag && world.isRaining() && this.a(world, blockposition) && random.nextFloat() < 0.2F + (float) i * 0.03F) {
-                world.setAir(blockposition);
+                fireExtinguished(world, blockposition); // CraftBukkit - extinguished by rain
             } else {
                 int j = Math.min(15, i + random.nextInt(3) / 2);
 
@@ -103,14 +126,14 @@
                     world.J().a(blockposition, this, this.a((IWorldReader) world) + random.nextInt(10));
                     if (!this.d(world, blockposition)) {
                         if (!world.getType(blockposition.down()).q() || i > 3) {
-                            world.setAir(blockposition);
+                            fireExtinguished(world, blockposition); // CraftBukkit
                         }
 
                         return;
                     }
 
                     if (i == 15 && random.nextInt(4) == 0 && !this.k(world.getType(blockposition.down()))) {
-                        world.setAir(blockposition);
+                        fireExtinguished(world, blockposition); // CraftBukkit
                         return;
                     }
                 }
@@ -118,12 +141,14 @@
                 boolean flag1 = world.x(blockposition);
                 int k = flag1 ? -50 : 0;
 
-                this.a(world, blockposition.east(), 300 + k, random, i);
-                this.a(world, blockposition.west(), 300 + k, random, i);
-                this.a(world, blockposition.down(), 250 + k, random, i);
-                this.a(world, blockposition.up(), 250 + k, random, i);
-                this.a(world, blockposition.north(), 300 + k, random, i);
-                this.a(world, blockposition.south(), 300 + k, random, i);
+                // CraftBukkit start - add source blockposition to burn calls
+                this.a(world, blockposition.east(), 300 + k, random, i, blockposition);
+                this.a(world, blockposition.west(), 300 + k, random, i, blockposition);
+                this.a(world, blockposition.down(), 250 + k, random, i, blockposition);
+                this.a(world, blockposition.up(), 250 + k, random, i, blockposition);
+                this.a(world, blockposition.north(), 300 + k, random, i, blockposition);
+                this.a(world, blockposition.south(), 300 + k, random, i, blockposition);
+                // CraftBukkit end
                 BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
 
                 for (int l = -1; l <= 1; ++l) {
@@ -149,7 +174,15 @@
                                     if (i2 > 0 && random.nextInt(k1) <= i2 && (!world.isRaining() || !this.a(world, (BlockPosition) blockposition_mutableblockposition))) {
                                         int j2 = Math.min(15, i + random.nextInt(5) / 4);
 
-                                        world.setTypeAndData(blockposition_mutableblockposition, (IBlockData) this.a((IBlockAccess) world, (BlockPosition) blockposition_mutableblockposition).set(BlockFire.AGE, Integer.valueOf(j2)), 3);
+                                        // CraftBukkit start - Call to stop spread of fire
+                                        if (world.getType(blockposition_mutableblockposition) != Blocks.FIRE) {
+                                            if (CraftEventFactory.callBlockIgniteEvent(world, blockposition_mutableblockposition, blockposition).isCancelled()) {
+                                                continue;
+                                            }
+
+                                            CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition_mutableblockposition, (IBlockData) this.a((IBlockAccess) world, (BlockPosition) blockposition_mutableblockposition).set(BlockFire.AGE, Integer.valueOf(j2)), 3); // CraftBukkit
+                                        }
+                                        // CraftBukkit end
                                     }
                                 }
                             }
@@ -173,12 +206,24 @@
         return this.flameChances.getInt(block);
     }
 
-    private void a(World world, BlockPosition blockposition, int i, Random random, int j) {
+    private void a(World world, BlockPosition blockposition, int i, Random random, int j, BlockPosition sourceposition) { // CraftBukkit add sourceposition
         int k = this.f(world.getType(blockposition).getBlock());
 
         if (random.nextInt(i) < k) {
             IBlockData iblockdata = world.getType(blockposition);
 
+            // CraftBukkit start
+            org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
+            org.bukkit.block.Block sourceBlock = world.getWorld().getBlockAt(sourceposition.getX(), sourceposition.getY(), sourceposition.getZ());
+
+            BlockBurnEvent event = new BlockBurnEvent(theBlock, sourceBlock);
+            world.getServer().getPluginManager().callEvent(event);
+
+            if (event.isCancelled()) {
+                return;
+            }
+            // CraftBukkit end
+
             if (random.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) {
                 int l = Math.min(j + random.nextInt(5) / 4, 15);
 
@@ -241,7 +286,7 @@
         if (iblockdata1.getBlock() != iblockdata.getBlock()) {
             if (world.worldProvider.getDimensionManager() != DimensionManager.OVERWORLD && world.worldProvider.getDimensionManager() != DimensionManager.NETHER || !((BlockPortal) Blocks.NETHER_PORTAL).a((GeneratorAccess) world, blockposition)) {
                 if (!iblockdata.canPlace(world, blockposition)) {
-                    world.setAir(blockposition);
+                    fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke
                 } else {
                     world.J().a(blockposition, this, this.a((IWorldReader) world) + world.random.nextInt(10));
                 }
@@ -387,4 +432,12 @@
         blockfire.a(Blocks.BLACK_CARPET, 60, 20);
         blockfire.a(Blocks.DRIED_KELP_BLOCK, 30, 60);
     }
+
+    // CraftBukkit start
+    private void fireExtinguished(GeneratorAccess world, BlockPosition position) {
+        if (!CraftEventFactory.callBlockFadeEvent(world, position, Blocks.AIR.getBlockData()).isCancelled()) {
+            world.setAir(position);
+        }
+    }
+    // CraftBukkit end
 }