--- a/net/minecraft/world/entity/projectile/ShulkerBullet.java
+++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java
@@ -7,6 +7,7 @@
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
+import net.minecraft.core.Holder;
 import net.minecraft.core.particles.ParticleTypes;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
@@ -30,9 +31,13 @@
 import net.minecraft.world.phys.EntityHitResult;
 import net.minecraft.world.phys.HitResult;
 import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.event.entity.EntityRemoveEvent;
+// CraftBukkit end
 
 public class ShulkerBullet extends Projectile {
-    private static final double SPEED = 0.15;
+
+    private static final double SPEED = 0.15D;
     @Nullable
     private Entity finalTarget;
     @Nullable
@@ -52,12 +57,26 @@
     public ShulkerBullet(Level world, LivingEntity owner, Entity target, Direction.Axis axis) {
         this(EntityType.SHULKER_BULLET, world);
         this.setOwner(owner);
-        Vec3 vec3 = owner.getBoundingBox().getCenter();
-        this.moveTo(vec3.x, vec3.y, vec3.z, this.getYRot(), this.getXRot());
+        Vec3 vec3d = owner.getBoundingBox().getCenter();
+
+        this.moveTo(vec3d.x, vec3d.y, vec3d.z, this.getYRot(), this.getXRot());
         this.finalTarget = target;
         this.currentMoveDirection = Direction.UP;
         this.selectNextMoveDirection(axis);
+        this.projectileSource = (org.bukkit.entity.LivingEntity) owner.getBukkitEntity(); // CraftBukkit
+    }
+
+    // CraftBukkit start
+    public Entity getTarget() {
+        return this.finalTarget;
+    }
+
+    public void setTarget(Entity e) {
+        this.finalTarget = e;
+        this.currentMoveDirection = Direction.UP;
+        this.selectNextMoveDirection(Direction.Axis.X);
     }
+    // CraftBukkit end
 
     @Override
     public SoundSource getSoundSource() {
@@ -95,11 +114,11 @@
         if (nbt.hasUUID("Target")) {
             this.targetId = nbt.getUUID("Target");
         }
+
     }
 
     @Override
-    protected void defineSynchedData(SynchedEntityData.Builder builder) {
-    }
+    protected void defineSynchedData(SynchedEntityData.Builder builder) {}
 
     @Nullable
     private Direction getMoveDirection() {
@@ -111,73 +130,77 @@
     }
 
     private void selectNextMoveDirection(@Nullable Direction.Axis axis) {
-        double d = 0.5;
-        BlockPos blockPos;
+        double d0 = 0.5D;
+        BlockPos blockposition;
+
         if (this.finalTarget == null) {
-            blockPos = this.blockPosition().below();
+            blockposition = this.blockPosition().below();
         } else {
-            d = (double)this.finalTarget.getBbHeight() * 0.5;
-            blockPos = BlockPos.containing(this.finalTarget.getX(), this.finalTarget.getY() + d, this.finalTarget.getZ());
+            d0 = (double) this.finalTarget.getBbHeight() * 0.5D;
+            blockposition = BlockPos.containing(this.finalTarget.getX(), this.finalTarget.getY() + d0, this.finalTarget.getZ());
         }
 
-        double e = (double)blockPos.getX() + 0.5;
-        double f = (double)blockPos.getY() + d;
-        double g = (double)blockPos.getZ() + 0.5;
-        Direction direction = null;
-        if (!blockPos.closerToCenterThan(this.position(), 2.0)) {
-            BlockPos blockPos3 = this.blockPosition();
+        double d1 = (double) blockposition.getX() + 0.5D;
+        double d2 = (double) blockposition.getY() + d0;
+        double d3 = (double) blockposition.getZ() + 0.5D;
+        Direction enumdirection = null;
+
+        if (!blockposition.closerToCenterThan(this.position(), 2.0D)) {
+            BlockPos blockposition1 = this.blockPosition();
             List<Direction> list = Lists.newArrayList();
+
             if (axis != Direction.Axis.X) {
-                if (blockPos3.getX() < blockPos.getX() && this.level().isEmptyBlock(blockPos3.east())) {
+                if (blockposition1.getX() < blockposition.getX() && this.level().isEmptyBlock(blockposition1.east())) {
                     list.add(Direction.EAST);
-                } else if (blockPos3.getX() > blockPos.getX() && this.level().isEmptyBlock(blockPos3.west())) {
+                } else if (blockposition1.getX() > blockposition.getX() && this.level().isEmptyBlock(blockposition1.west())) {
                     list.add(Direction.WEST);
                 }
             }
 
             if (axis != Direction.Axis.Y) {
-                if (blockPos3.getY() < blockPos.getY() && this.level().isEmptyBlock(blockPos3.above())) {
+                if (blockposition1.getY() < blockposition.getY() && this.level().isEmptyBlock(blockposition1.above())) {
                     list.add(Direction.UP);
-                } else if (blockPos3.getY() > blockPos.getY() && this.level().isEmptyBlock(blockPos3.below())) {
+                } else if (blockposition1.getY() > blockposition.getY() && this.level().isEmptyBlock(blockposition1.below())) {
                     list.add(Direction.DOWN);
                 }
             }
 
             if (axis != Direction.Axis.Z) {
-                if (blockPos3.getZ() < blockPos.getZ() && this.level().isEmptyBlock(blockPos3.south())) {
+                if (blockposition1.getZ() < blockposition.getZ() && this.level().isEmptyBlock(blockposition1.south())) {
                     list.add(Direction.SOUTH);
-                } else if (blockPos3.getZ() > blockPos.getZ() && this.level().isEmptyBlock(blockPos3.north())) {
+                } else if (blockposition1.getZ() > blockposition.getZ() && this.level().isEmptyBlock(blockposition1.north())) {
                     list.add(Direction.NORTH);
                 }
             }
 
-            direction = Direction.getRandom(this.random);
+            enumdirection = Direction.getRandom(this.random);
             if (list.isEmpty()) {
-                for (int i = 5; !this.level().isEmptyBlock(blockPos3.relative(direction)) && i > 0; i--) {
-                    direction = Direction.getRandom(this.random);
+                for (int i = 5; !this.level().isEmptyBlock(blockposition1.relative(enumdirection)) && i > 0; --i) {
+                    enumdirection = Direction.getRandom(this.random);
                 }
             } else {
-                direction = list.get(this.random.nextInt(list.size()));
+                enumdirection = (Direction) list.get(this.random.nextInt(list.size()));
             }
 
-            e = this.getX() + (double)direction.getStepX();
-            f = this.getY() + (double)direction.getStepY();
-            g = this.getZ() + (double)direction.getStepZ();
+            d1 = this.getX() + (double) enumdirection.getStepX();
+            d2 = this.getY() + (double) enumdirection.getStepY();
+            d3 = this.getZ() + (double) enumdirection.getStepZ();
         }
 
-        this.setMoveDirection(direction);
-        double h = e - this.getX();
-        double j = f - this.getY();
-        double k = g - this.getZ();
-        double l = Math.sqrt(h * h + j * j + k * k);
-        if (l == 0.0) {
-            this.targetDeltaX = 0.0;
-            this.targetDeltaY = 0.0;
-            this.targetDeltaZ = 0.0;
+        this.setMoveDirection(enumdirection);
+        double d4 = d1 - this.getX();
+        double d5 = d2 - this.getY();
+        double d6 = d3 - this.getZ();
+        double d7 = Math.sqrt(d4 * d4 + d5 * d5 + d6 * d6);
+
+        if (d7 == 0.0D) {
+            this.targetDeltaX = 0.0D;
+            this.targetDeltaY = 0.0D;
+            this.targetDeltaZ = 0.0D;
         } else {
-            this.targetDeltaX = h / l * 0.15;
-            this.targetDeltaY = j / l * 0.15;
-            this.targetDeltaZ = k / l * 0.15;
+            this.targetDeltaX = d4 / d7 * 0.15D;
+            this.targetDeltaY = d5 / d7 * 0.15D;
+            this.targetDeltaZ = d6 / d7 * 0.15D;
         }
 
         this.hasImpulse = true;
@@ -187,71 +210,76 @@
     @Override
     public void checkDespawn() {
         if (this.level().getDifficulty() == Difficulty.PEACEFUL) {
-            this.discard();
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
         }
+
     }
 
     @Override
     protected double getDefaultGravity() {
-        return 0.04;
+        return 0.04D;
     }
 
     @Override
     public void tick() {
         super.tick();
+        Vec3 vec3d;
+
         if (!this.level().isClientSide) {
             if (this.finalTarget == null && this.targetId != null) {
-                this.finalTarget = ((ServerLevel)this.level()).getEntity(this.targetId);
+                this.finalTarget = ((ServerLevel) this.level()).getEntity(this.targetId);
                 if (this.finalTarget == null) {
                     this.targetId = null;
                 }
             }
 
-            if (this.finalTarget == null || !this.finalTarget.isAlive() || this.finalTarget instanceof Player && this.finalTarget.isSpectator()) {
-                this.applyGravity();
+            if (this.finalTarget != null && this.finalTarget.isAlive() && (!(this.finalTarget instanceof Player) || !this.finalTarget.isSpectator())) {
+                this.targetDeltaX = Mth.clamp(this.targetDeltaX * 1.025D, -1.0D, 1.0D);
+                this.targetDeltaY = Mth.clamp(this.targetDeltaY * 1.025D, -1.0D, 1.0D);
+                this.targetDeltaZ = Mth.clamp(this.targetDeltaZ * 1.025D, -1.0D, 1.0D);
+                vec3d = this.getDeltaMovement();
+                this.setDeltaMovement(vec3d.add((this.targetDeltaX - vec3d.x) * 0.2D, (this.targetDeltaY - vec3d.y) * 0.2D, (this.targetDeltaZ - vec3d.z) * 0.2D));
             } else {
-                this.targetDeltaX = Mth.clamp(this.targetDeltaX * 1.025, -1.0, 1.0);
-                this.targetDeltaY = Mth.clamp(this.targetDeltaY * 1.025, -1.0, 1.0);
-                this.targetDeltaZ = Mth.clamp(this.targetDeltaZ * 1.025, -1.0, 1.0);
-                Vec3 vec3 = this.getDeltaMovement();
-                this.setDeltaMovement(vec3.add((this.targetDeltaX - vec3.x) * 0.2, (this.targetDeltaY - vec3.y) * 0.2, (this.targetDeltaZ - vec3.z) * 0.2));
+                this.applyGravity();
             }
 
-            HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
-            if (hitResult.getType() != HitResult.Type.MISS) {
-                this.hitTargetOrDeflectSelf(hitResult);
+            HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+
+            if (movingobjectposition.getType() != HitResult.Type.MISS) {
+                this.preHitTargetOrDeflectSelf(movingobjectposition); // CraftBukkit - projectile hit event
             }
         }
 
         this.checkInsideBlocks();
-        Vec3 vec32 = this.getDeltaMovement();
-        this.setPos(this.getX() + vec32.x, this.getY() + vec32.y, this.getZ() + vec32.z);
+        vec3d = this.getDeltaMovement();
+        this.setPos(this.getX() + vec3d.x, this.getY() + vec3d.y, this.getZ() + vec3d.z);
         ProjectileUtil.rotateTowardsMovement(this, 0.5F);
         if (this.level().isClientSide) {
-            this.level().addParticle(ParticleTypes.END_ROD, this.getX() - vec32.x, this.getY() - vec32.y + 0.15, this.getZ() - vec32.z, 0.0, 0.0, 0.0);
+            this.level().addParticle(ParticleTypes.END_ROD, this.getX() - vec3d.x, this.getY() - vec3d.y + 0.15D, this.getZ() - vec3d.z, 0.0D, 0.0D, 0.0D);
         } else if (this.finalTarget != null && !this.finalTarget.isRemoved()) {
             if (this.flightSteps > 0) {
-                this.flightSteps--;
+                --this.flightSteps;
                 if (this.flightSteps == 0) {
                     this.selectNextMoveDirection(this.currentMoveDirection == null ? null : this.currentMoveDirection.getAxis());
                 }
             }
 
             if (this.currentMoveDirection != null) {
-                BlockPos blockPos = this.blockPosition();
-                Direction.Axis axis = this.currentMoveDirection.getAxis();
-                if (this.level().loadedAndEntityCanStandOn(blockPos.relative(this.currentMoveDirection), this)) {
-                    this.selectNextMoveDirection(axis);
+                BlockPos blockposition = this.blockPosition();
+                Direction.Axis enumdirection_enumaxis = this.currentMoveDirection.getAxis();
+
+                if (this.level().loadedAndEntityCanStandOn(blockposition.relative(this.currentMoveDirection), this)) {
+                    this.selectNextMoveDirection(enumdirection_enumaxis);
                 } else {
-                    BlockPos blockPos2 = this.finalTarget.blockPosition();
-                    if (axis == Direction.Axis.X && blockPos.getX() == blockPos2.getX()
-                        || axis == Direction.Axis.Z && blockPos.getZ() == blockPos2.getZ()
-                        || axis == Direction.Axis.Y && blockPos.getY() == blockPos2.getY()) {
-                        this.selectNextMoveDirection(axis);
+                    BlockPos blockposition1 = this.finalTarget.blockPosition();
+
+                    if (enumdirection_enumaxis == Direction.Axis.X && blockposition.getX() == blockposition1.getX() || enumdirection_enumaxis == Direction.Axis.Z && blockposition.getZ() == blockposition1.getZ() || enumdirection_enumaxis == Direction.Axis.Y && blockposition.getY() == blockposition1.getY()) {
+                        this.selectNextMoveDirection(enumdirection_enumaxis);
                     }
                 }
             }
         }
+
     }
 
     @Override
@@ -266,7 +294,7 @@
 
     @Override
     public boolean shouldRenderAtSqrDistance(double distance) {
-        return distance < 16384.0;
+        return distance < 16384.0D;
     }
 
     @Override
@@ -278,37 +306,51 @@
     protected void onHitEntity(EntityHitResult entityHitResult) {
         super.onHitEntity(entityHitResult);
         Entity entity = entityHitResult.getEntity();
-        Entity entity2 = this.getOwner();
-        LivingEntity livingEntity = entity2 instanceof LivingEntity ? (LivingEntity)entity2 : null;
-        DamageSource damageSource = this.damageSources().mobProjectile(this, livingEntity);
-        boolean bl = entity.hurt(damageSource, 4.0F);
-        if (bl) {
-            if (this.level() instanceof ServerLevel serverLevel) {
-                EnchantmentHelper.doPostAttackEffects(serverLevel, entity, damageSource);
+        Entity entity1 = this.getOwner();
+        LivingEntity entityliving = entity1 instanceof LivingEntity ? (LivingEntity) entity1 : null;
+        DamageSource damagesource = this.damageSources().mobProjectile(this, entityliving);
+        boolean flag = entity.hurt(damagesource, 4.0F);
+
+        if (flag) {
+            Level world = this.level();
+
+            if (world instanceof ServerLevel) {
+                ServerLevel worldserver = (ServerLevel) world;
+
+                EnchantmentHelper.doPostAttackEffects(worldserver, entity, damagesource);
             }
 
-            if (entity instanceof LivingEntity livingEntity2) {
-                livingEntity2.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(entity2, this));
+            if (entity instanceof LivingEntity) {
+                LivingEntity entityliving1 = (LivingEntity) entity;
+
+                entityliving1.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), (Entity) MoreObjects.firstNonNull(entity1, this), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
             }
         }
+
     }
 
     @Override
     protected void onHitBlock(BlockHitResult blockHitResult) {
         super.onHitBlock(blockHitResult);
-        ((ServerLevel)this.level()).sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(), this.getZ(), 2, 0.2, 0.2, 0.2, 0.0);
+        ((ServerLevel) this.level()).sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(), this.getZ(), 2, 0.2D, 0.2D, 0.2D, 0.0D);
         this.playSound(SoundEvents.SHULKER_BULLET_HIT, 1.0F, 1.0F);
     }
 
     private void destroy() {
-        this.discard();
-        this.level().gameEvent(GameEvent.ENTITY_DAMAGE, this.position(), GameEvent.Context.of(this));
+        // CraftBukkit start - add Bukkit remove cause
+        this.destroy(null);
+    }
+
+    private void destroy(EntityRemoveEvent.Cause cause) {
+        this.discard(cause);
+        // CraftBukkit end
+        this.level().gameEvent((Holder) GameEvent.ENTITY_DAMAGE, this.position(), GameEvent.Context.of((Entity) this));
     }
 
     @Override
     protected void onHit(HitResult hitResult) {
         super.onHit(hitResult);
-        this.destroy();
+        this.destroy(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
     }
 
     @Override
@@ -318,10 +360,15 @@
 
     @Override
     public boolean hurt(DamageSource source, float amount) {
+        // CraftBukkit start
+        if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount, false)) {
+            return false;
+        }
+        // CraftBukkit end
         if (!this.level().isClientSide) {
             this.playSound(SoundEvents.SHULKER_BULLET_HURT, 1.0F, 1.0F);
-            ((ServerLevel)this.level()).sendParticles(ParticleTypes.CRIT, this.getX(), this.getY(), this.getZ(), 15, 0.2, 0.2, 0.2, 0.0);
-            this.destroy();
+            ((ServerLevel) this.level()).sendParticles(ParticleTypes.CRIT, this.getX(), this.getY(), this.getZ(), 15, 0.2D, 0.2D, 0.2D, 0.0D);
+            this.destroy(EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
         }
 
         return true;
@@ -330,9 +377,10 @@
     @Override
     public void recreateFromPacket(ClientboundAddEntityPacket packet) {
         super.recreateFromPacket(packet);
-        double d = packet.getXa();
-        double e = packet.getYa();
-        double f = packet.getZa();
-        this.setDeltaMovement(d, e, f);
+        double d0 = packet.getXa();
+        double d1 = packet.getYa();
+        double d2 = packet.getZa();
+
+        this.setDeltaMovement(d0, d1, d2);
     }
 }
