--- a/net/minecraft/world/entity/LightningBolt.java
+++ b/net/minecraft/world/entity/LightningBolt.java
@@ -1,6 +1,8 @@
 package net.minecraft.world.entity;
 
 import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -8,6 +10,8 @@
 import javax.annotation.Nullable;
 import net.minecraft.advancements.CriteriaTriggers;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.core.Vec3i;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.syncher.SynchedEntityData;
 import net.minecraft.server.level.ServerLevel;
@@ -25,11 +29,16 @@
 import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.phys.AABB;
 import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+// CraftBukkit end
 
 public class LightningBolt extends Entity {
+
     private static final int START_LIFE = 2;
-    private static final double DAMAGE_RADIUS = 3.0;
-    private static final double DETECTION_RADIUS = 15.0;
+    private static final double DAMAGE_RADIUS = 3.0D;
+    private static final double DETECTION_RADIUS = 15.0D;
     public int life;
     public long seed;
     public int flashes;
@@ -38,6 +47,7 @@
     private ServerPlayer cause;
     private final Set<Entity> hitEntities = Sets.newHashSet();
     private int blocksSetOnFire;
+    public boolean isEffect; // Paper - Properly handle lightning effects api
 
     public LightningBolt(EntityType<? extends LightningBolt> type, Level world) {
         super(type, world);
@@ -66,194 +76,210 @@
     }
 
     private void powerLightningRod() {
-        BlockPos blockPos = this.getStrikePosition();
-        BlockState blockState = this.level().getBlockState(blockPos);
-        if (blockState.is(Blocks.LIGHTNING_ROD)) {
-            ((LightningRodBlock)blockState.getBlock()).onLightningStrike(blockState, this.level(), blockPos);
+        BlockPos blockposition = this.getStrikePosition();
+        BlockState iblockdata = this.level().getBlockState(blockposition);
+
+        if (iblockdata.is(Blocks.LIGHTNING_ROD)) {
+            ((LightningRodBlock) iblockdata.getBlock()).onLightningStrike(iblockdata, this.level(), blockposition);
         }
+
     }
 
     @Override
     public void tick() {
         super.tick();
-        if (this.life == 2) {
+        if (!this.isEffect && this.life == 2) { // Paper - Properly handle lightning effects api
             if (this.level().isClientSide()) {
-                this.level()
-                    .playLocalSound(
-                        this.getX(),
-                        this.getY(),
-                        this.getZ(),
-                        SoundEvents.LIGHTNING_BOLT_THUNDER,
-                        SoundSource.WEATHER,
-                        10000.0F,
-                        0.8F + this.random.nextFloat() * 0.2F,
-                        false
-                    );
-                this.level()
-                    .playLocalSound(
-                        this.getX(),
-                        this.getY(),
-                        this.getZ(),
-                        SoundEvents.LIGHTNING_BOLT_IMPACT,
-                        SoundSource.WEATHER,
-                        2.0F,
-                        0.5F + this.random.nextFloat() * 0.2F,
-                        false
-                    );
+                this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_THUNDER, SoundSource.WEATHER, 10000.0F, 0.8F + this.random.nextFloat() * 0.2F, false);
+                this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_IMPACT, SoundSource.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F, false);
             } else {
-                Difficulty difficulty = this.level().getDifficulty();
-                if (difficulty == Difficulty.NORMAL || difficulty == Difficulty.HARD) {
+                Difficulty enumdifficulty = this.level().getDifficulty();
+
+                if (enumdifficulty == Difficulty.NORMAL || enumdifficulty == Difficulty.HARD) {
                     this.spawnFire(4);
                 }
 
                 this.powerLightningRod();
-                clearCopperOnLightningStrike(this.level(), this.getStrikePosition());
+                LightningBolt.clearCopperOnLightningStrike(this.level(), this.getStrikePosition(), this); // Paper - Call EntityChangeBlockEvent
                 this.gameEvent(GameEvent.LIGHTNING_STRIKE);
             }
         }
 
-        this.life--;
+        --this.life;
+        List list;
+        Iterator iterator;
+
         if (this.life < 0) {
             if (this.flashes == 0) {
                 if (this.level() instanceof ServerLevel) {
-                    List<Entity> list = this.level()
-                        .getEntities(
-                            this,
-                            new AABB(
-                                this.getX() - 15.0, this.getY() - 15.0, this.getZ() - 15.0, this.getX() + 15.0, this.getY() + 6.0 + 15.0, this.getZ() + 15.0
-                            ),
-                            entityx -> entityx.isAlive() && !this.hitEntities.contains(entityx)
-                        );
+                    list = this.level().getEntities((Entity) this, new AABB(this.getX() - 15.0D, this.getY() - 15.0D, this.getZ() - 15.0D, this.getX() + 15.0D, this.getY() + 6.0D + 15.0D, this.getZ() + 15.0D), (entity) -> {
+                        return entity.isAlive() && !this.hitEntities.contains(entity);
+                    });
+                    iterator = ((ServerLevel) this.level()).getPlayers((entityplayer) -> {
+                        return entityplayer.distanceTo(this) < 256.0F;
+                    }).iterator();
 
-                    for (ServerPlayer serverPlayer : ((ServerLevel)this.level()).getPlayers(serverPlayerx -> serverPlayerx.distanceTo(this) < 256.0F)) {
-                        CriteriaTriggers.LIGHTNING_STRIKE.trigger(serverPlayer, this, list);
+                    while (iterator.hasNext()) {
+                        ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+
+                        CriteriaTriggers.LIGHTNING_STRIKE.trigger(entityplayer, this, list);
                     }
                 }
 
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
             } else if (this.life < -this.random.nextInt(10)) {
-                this.flashes--;
+                --this.flashes;
                 this.life = 1;
                 this.seed = this.random.nextLong();
                 this.spawnFire(0);
             }
         }
 
-        if (this.life >= 0) {
+        if (this.life >= 0 && !this.isEffect) { // CraftBukkit - add !this.visualOnly // Paper - Properly handle lightning effects api
             if (!(this.level() instanceof ServerLevel)) {
                 this.level().setSkyFlashTime(2);
             } else if (!this.visualOnly) {
-                List<Entity> list2 = this.level()
-                    .getEntities(
-                        this,
-                        new AABB(this.getX() - 3.0, this.getY() - 3.0, this.getZ() - 3.0, this.getX() + 3.0, this.getY() + 6.0 + 3.0, this.getZ() + 3.0),
-                        Entity::isAlive
-                    );
+                list = this.level().getEntities((Entity) this, new AABB(this.getX() - 3.0D, this.getY() - 3.0D, this.getZ() - 3.0D, this.getX() + 3.0D, this.getY() + 6.0D + 3.0D, this.getZ() + 3.0D), Entity::isAlive);
+                iterator = list.iterator();
 
-                for (Entity entity : list2) {
-                    entity.thunderHit((ServerLevel)this.level(), this);
+                while (iterator.hasNext()) {
+                    Entity entity = (Entity) iterator.next();
+
+                    entity.thunderHit((ServerLevel) this.level(), this);
                 }
 
-                this.hitEntities.addAll(list2);
+                this.hitEntities.addAll(list);
                 if (this.cause != null) {
-                    CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, list2);
+                    CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, (Collection) list);
                 }
             }
         }
+
     }
 
     private BlockPos getStrikePosition() {
-        Vec3 vec3 = this.position();
-        return BlockPos.containing(vec3.x, vec3.y - 1.0E-6, vec3.z);
+        Vec3 vec3d = this.position();
+
+        return BlockPos.containing(vec3d.x, vec3d.y - 1.0E-6D, vec3d.z);
     }
 
     private void spawnFire(int spreadAttempts) {
-        if (!this.visualOnly && !this.level().isClientSide && this.level().getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) {
-            BlockPos blockPos = this.blockPosition();
-            BlockState blockState = BaseFireBlock.getState(this.level(), blockPos);
-            if (this.level().getBlockState(blockPos).isAir() && blockState.canSurvive(this.level(), blockPos)) {
-                this.level().setBlockAndUpdate(blockPos, blockState);
-                this.blocksSetOnFire++;
+        if (!this.visualOnly && !this.isEffect && !this.level().isClientSide && this.level().getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { // Paper - Properly handle lightning effects api
+            BlockPos blockposition = this.blockPosition();
+            BlockState iblockdata = BaseFireBlock.getState(this.level(), blockposition);
+
+            if (this.level().getBlockState(blockposition).isAir() && iblockdata.canSurvive(this.level(), blockposition)) {
+                // CraftBukkit start - add "!visualOnly"
+                if (!this.visualOnly && !CraftEventFactory.callBlockIgniteEvent(this.level(), blockposition, this).isCancelled()) {
+                    this.level().setBlockAndUpdate(blockposition, iblockdata);
+                    ++this.blocksSetOnFire;
+                }
+                // CraftBukkit end
             }
 
-            for (int i = 0; i < spreadAttempts; i++) {
-                BlockPos blockPos2 = blockPos.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1);
-                blockState = BaseFireBlock.getState(this.level(), blockPos2);
-                if (this.level().getBlockState(blockPos2).isAir() && blockState.canSurvive(this.level(), blockPos2)) {
-                    this.level().setBlockAndUpdate(blockPos2, blockState);
-                    this.blocksSetOnFire++;
+            for (int j = 0; j < spreadAttempts; ++j) {
+                BlockPos blockposition1 = blockposition.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1);
+
+                iblockdata = BaseFireBlock.getState(this.level(), blockposition1);
+                if (this.level().getBlockState(blockposition1).isAir() && iblockdata.canSurvive(this.level(), blockposition1)) {
+                    // CraftBukkit start - add "!visualOnly"
+                    if (!this.visualOnly && !CraftEventFactory.callBlockIgniteEvent(this.level(), blockposition1, this).isCancelled()) {
+                        this.level().setBlockAndUpdate(blockposition1, iblockdata);
+                        ++this.blocksSetOnFire;
+                    }
+                    // CraftBukkit end
                 }
             }
+
         }
     }
 
-    private static void clearCopperOnLightningStrike(Level world, BlockPos pos) {
-        BlockState blockState = world.getBlockState(pos);
-        BlockPos blockPos;
-        BlockState blockState2;
-        if (blockState.is(Blocks.LIGHTNING_ROD)) {
-            blockPos = pos.relative(blockState.getValue(LightningRodBlock.FACING).getOpposite());
-            blockState2 = world.getBlockState(blockPos);
+    private static void clearCopperOnLightningStrike(Level world, BlockPos pos, Entity lightning) { // Paper - Call EntityChangeBlockEvent
+        BlockState iblockdata = world.getBlockState(pos);
+        BlockPos blockposition1;
+        BlockState iblockdata1;
+
+        if (iblockdata.is(Blocks.LIGHTNING_ROD)) {
+            blockposition1 = pos.relative(((Direction) iblockdata.getValue(LightningRodBlock.FACING)).getOpposite());
+            iblockdata1 = world.getBlockState(blockposition1);
         } else {
-            blockPos = pos;
-            blockState2 = blockState;
+            blockposition1 = pos;
+            iblockdata1 = iblockdata;
         }
 
-        if (blockState2.getBlock() instanceof WeatheringCopper) {
-            world.setBlockAndUpdate(blockPos, WeatheringCopper.getFirst(world.getBlockState(blockPos)));
-            BlockPos.MutableBlockPos mutableBlockPos = pos.mutable();
+        if (iblockdata1.getBlock() instanceof WeatheringCopper) {
+            // Paper start - Call EntityChangeBlockEvent
+            BlockState newBlock = WeatheringCopper.getFirst(world.getBlockState(blockposition1));
+            if (CraftEventFactory.callEntityChangeBlockEvent(lightning, blockposition1, newBlock)) {
+                world.setBlockAndUpdate(blockposition1, newBlock);
+            }
+            // Paper end - Call EntityChangeBlockEvent
+            BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable();
             int i = world.random.nextInt(3) + 3;
 
-            for (int j = 0; j < i; j++) {
+            for (int j = 0; j < i; ++j) {
                 int k = world.random.nextInt(8) + 1;
-                randomWalkCleaningCopper(world, blockPos, mutableBlockPos, k);
+
+                LightningBolt.randomWalkCleaningCopper(world, blockposition1, blockposition_mutableblockposition, k, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent
             }
+
         }
     }
 
-    private static void randomWalkCleaningCopper(Level world, BlockPos pos, BlockPos.MutableBlockPos mutablePos, int count) {
+    private static void randomWalkCleaningCopper(Level world, BlockPos pos, BlockPos.MutableBlockPos mutablePos, int count, Entity lightning) { // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent
         mutablePos.set(pos);
 
-        for (int i = 0; i < count; i++) {
-            Optional<BlockPos> optional = randomStepCleaningCopper(world, mutablePos);
+        for (int j = 0; j < count; ++j) {
+            Optional<BlockPos> optional = LightningBolt.randomStepCleaningCopper(world, mutablePos, lightning); // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent
+
             if (optional.isEmpty()) {
                 break;
             }
 
-            mutablePos.set(optional.get());
+            mutablePos.set((Vec3i) optional.get());
         }
+
     }
 
-    private static Optional<BlockPos> randomStepCleaningCopper(Level world, BlockPos pos) {
-        for (BlockPos blockPos : BlockPos.randomInCube(world.random, 10, pos, 1)) {
-            BlockState blockState = world.getBlockState(blockPos);
-            if (blockState.getBlock() instanceof WeatheringCopper) {
-                WeatheringCopper.getPrevious(blockState).ifPresent(state -> world.setBlockAndUpdate(blockPos, state));
-                world.levelEvent(3002, blockPos, -1);
-                return Optional.of(blockPos);
+    private static Optional<BlockPos> randomStepCleaningCopper(Level world, BlockPos pos, Entity lightning) { // Paper - transmit LightningBolt instance to call EntityChangeBlockEvent
+        Iterator iterator = BlockPos.randomInCube(world.random, 10, pos, 1).iterator();
+
+        BlockPos blockposition1;
+        BlockState iblockdata;
+
+        do {
+            if (!iterator.hasNext()) {
+                return Optional.empty();
             }
-        }
 
-        return Optional.empty();
+            blockposition1 = (BlockPos) iterator.next();
+            iblockdata = world.getBlockState(blockposition1);
+        } while (!(iblockdata.getBlock() instanceof WeatheringCopper));
+
+        BlockPos blockposition1Final = blockposition1; // CraftBukkit - decompile error
+        WeatheringCopper.getPrevious(iblockdata).ifPresent((iblockdata1) -> {
+            if (CraftEventFactory.callEntityChangeBlockEvent(lightning, blockposition1Final, iblockdata1)) // Paper - call EntityChangeBlockEvent
+            world.setBlockAndUpdate(blockposition1Final, iblockdata1); // CraftBukkit - decompile error
+        });
+        world.levelEvent(3002, blockposition1, -1);
+        return Optional.of(blockposition1);
     }
 
     @Override
     public boolean shouldRenderAtSqrDistance(double distance) {
-        double d = 64.0 * getViewScale();
-        return distance < d * d;
+        double d1 = 64.0D * getViewScale();
+
+        return distance < d1 * d1;
     }
 
     @Override
-    protected void defineSynchedData(SynchedEntityData.Builder builder) {
-    }
+    protected void defineSynchedData(SynchedEntityData.Builder builder) {}
 
     @Override
-    protected void readAdditionalSaveData(CompoundTag nbt) {
-    }
+    protected void readAdditionalSaveData(CompoundTag nbt) {}
 
     @Override
-    protected void addAdditionalSaveData(CompoundTag nbt) {
-    }
+    protected void addAdditionalSaveData(CompoundTag nbt) {}
 
     public int getBlocksSetOnFire() {
         return this.blocksSetOnFire;
