--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
@@ -1,5 +1,6 @@
 package net.minecraft.world.entity.projectile;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.OptionalInt;
 import javax.annotation.Nullable;
@@ -16,6 +17,7 @@
 import net.minecraft.world.entity.EntityType;
 import net.minecraft.world.entity.LivingEntity;
 import net.minecraft.world.entity.MoverType;
+import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.Items;
 import net.minecraft.world.item.component.FireworkExplosion;
@@ -27,19 +29,20 @@
 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 FireworkRocketEntity extends Projectile implements ItemSupplier {
-    public static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(
-        FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK
-    );
-    public static final EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET = SynchedEntityData.defineId(
-        FireworkRocketEntity.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT
-    );
+
+    public static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK);
+    public static final EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT);
     public static final EntityDataAccessor<Boolean> DATA_SHOT_AT_ANGLE = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.BOOLEAN);
     public int life;
     public int lifetime;
     @Nullable
     public LivingEntity attachedToEntity;
+    @Nullable public java.util.UUID spawningEntity; // Paper
 
     public FireworkRocketEntity(EntityType<? extends FireworkRocketEntity> type, Level world) {
         super(type, world);
@@ -49,14 +52,15 @@
         super(EntityType.FIREWORK_ROCKET, world);
         this.life = 0;
         this.setPos(x, y, z);
-        this.entityData.set(DATA_ID_FIREWORKS_ITEM, stack.copy());
+        this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, stack.copy());
         int i = 1;
-        Fireworks fireworks = stack.get(DataComponents.FIREWORKS);
+        Fireworks fireworks = (Fireworks) stack.get(DataComponents.FIREWORKS);
+
         if (fireworks != null) {
             i += fireworks.flightDuration();
         }
 
-        this.setDeltaMovement(this.random.triangle(0.0, 0.002297), 0.05, this.random.triangle(0.0, 0.002297));
+        this.setDeltaMovement(this.random.triangle(0.0D, 0.002297D), 0.05D, this.random.triangle(0.0D, 0.002297D));
         this.lifetime = 10 * i + this.random.nextInt(6) + this.random.nextInt(7);
     }
 
@@ -67,13 +71,13 @@
 
     public FireworkRocketEntity(Level world, ItemStack stack, LivingEntity shooter) {
         this(world, shooter, shooter.getX(), shooter.getY(), shooter.getZ(), stack);
-        this.entityData.set(DATA_ATTACHED_TO_TARGET, OptionalInt.of(shooter.getId()));
+        this.entityData.set(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET, OptionalInt.of(shooter.getId()));
         this.attachedToEntity = shooter;
     }
 
     public FireworkRocketEntity(Level world, ItemStack stack, double x, double y, double z, boolean shotAtAngle) {
         this(world, x, y, z, stack);
-        this.entityData.set(DATA_SHOT_AT_ANGLE, shotAtAngle);
+        this.entityData.set(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, shotAtAngle);
     }
 
     public FireworkRocketEntity(Level world, ItemStack stack, Entity entity, double x, double y, double z, boolean shotAtAngle) {
@@ -81,16 +85,32 @@
         this.setOwner(entity);
     }
 
+    // Spigot Start - copied from tick
+    @Override
+    public void inactiveTick() {
+        this.life += 1;
+
+        if (!this.level().isClientSide && this.life > this.lifetime) {
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
+        }
+        super.inactiveTick();
+    }
+    // Spigot End
+
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
-        builder.define(DATA_ID_FIREWORKS_ITEM, getDefaultItem());
-        builder.define(DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
-        builder.define(DATA_SHOT_AT_ANGLE, false);
+        builder.define(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, FireworkRocketEntity.getDefaultItem());
+        builder.define(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
+        builder.define(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, false);
     }
 
     @Override
     public boolean shouldRenderAtSqrDistance(double distance) {
-        return distance < 4096.0 && !this.isAttachedToEntity();
+        return distance < 4096.0D && !this.isAttachedToEntity();
     }
 
     @Override
@@ -101,101 +121,105 @@
     @Override
     public void tick() {
         super.tick();
+        Vec3 vec3d;
+
         if (this.isAttachedToEntity()) {
             if (this.attachedToEntity == null) {
-                this.entityData.get(DATA_ATTACHED_TO_TARGET).ifPresent(id -> {
-                    Entity entity = this.level().getEntity(id);
+                ((OptionalInt) this.entityData.get(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET)).ifPresent((i) -> {
+                    Entity entity = this.level().getEntity(i);
+
                     if (entity instanceof LivingEntity) {
-                        this.attachedToEntity = (LivingEntity)entity;
+                        this.attachedToEntity = (LivingEntity) entity;
                     }
+
                 });
             }
 
             if (this.attachedToEntity != null) {
-                Vec3 vec33;
                 if (this.attachedToEntity.isFallFlying()) {
-                    Vec3 vec3 = this.attachedToEntity.getLookAngle();
-                    double d = 1.5;
-                    double e = 0.1;
-                    Vec3 vec32 = this.attachedToEntity.getDeltaMovement();
-                    this.attachedToEntity
-                        .setDeltaMovement(
-                            vec32.add(
-                                vec3.x * 0.1 + (vec3.x * 1.5 - vec32.x) * 0.5,
-                                vec3.y * 0.1 + (vec3.y * 1.5 - vec32.y) * 0.5,
-                                vec3.z * 0.1 + (vec3.z * 1.5 - vec32.z) * 0.5
-                            )
-                        );
-                    vec33 = this.attachedToEntity.getHandHoldingItemAngle(Items.FIREWORK_ROCKET);
+                    Vec3 vec3d1 = this.attachedToEntity.getLookAngle();
+                    double d0 = 1.5D;
+                    double d1 = 0.1D;
+                    Vec3 vec3d2 = this.attachedToEntity.getDeltaMovement();
+
+                    this.attachedToEntity.setDeltaMovement(vec3d2.add(vec3d1.x * 0.1D + (vec3d1.x * 1.5D - vec3d2.x) * 0.5D, vec3d1.y * 0.1D + (vec3d1.y * 1.5D - vec3d2.y) * 0.5D, vec3d1.z * 0.1D + (vec3d1.z * 1.5D - vec3d2.z) * 0.5D));
+                    vec3d = this.attachedToEntity.getHandHoldingItemAngle(Items.FIREWORK_ROCKET);
                 } else {
-                    vec33 = Vec3.ZERO;
+                    vec3d = Vec3.ZERO;
                 }
 
-                this.setPos(this.attachedToEntity.getX() + vec33.x, this.attachedToEntity.getY() + vec33.y, this.attachedToEntity.getZ() + vec33.z);
+                this.setPos(this.attachedToEntity.getX() + vec3d.x, this.attachedToEntity.getY() + vec3d.y, this.attachedToEntity.getZ() + vec3d.z);
                 this.setDeltaMovement(this.attachedToEntity.getDeltaMovement());
             }
         } else {
             if (!this.isShotAtAngle()) {
-                double f = this.horizontalCollision ? 1.0 : 1.15;
-                this.setDeltaMovement(this.getDeltaMovement().multiply(f, 1.0, f).add(0.0, 0.04, 0.0));
+                double d2 = this.horizontalCollision ? 1.0D : 1.15D;
+
+                this.setDeltaMovement(this.getDeltaMovement().multiply(d2, 1.0D, d2).add(0.0D, 0.04D, 0.0D));
             }
 
-            Vec3 vec35 = this.getDeltaMovement();
-            this.move(MoverType.SELF, vec35);
-            this.setDeltaMovement(vec35);
+            vec3d = this.getDeltaMovement();
+            this.move(MoverType.SELF, vec3d);
+            this.setDeltaMovement(vec3d);
         }
 
-        HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+        HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+
         if (!this.noPhysics) {
-            this.hitTargetOrDeflectSelf(hitResult);
+            this.preHitTargetOrDeflectSelf(movingobjectposition); // CraftBukkit - projectile hit event
             this.hasImpulse = true;
         }
 
         this.updateRotation();
         if (this.life == 0 && !this.isSilent()) {
-            this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.FIREWORK_ROCKET_LAUNCH, SoundSource.AMBIENT, 3.0F, 1.0F);
+            this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.FIREWORK_ROCKET_LAUNCH, SoundSource.AMBIENT, 3.0F, 1.0F);
         }
 
-        this.life++;
+        ++this.life;
         if (this.level().isClientSide && this.life % 2 < 2) {
-            this.level()
-                .addParticle(
-                    ParticleTypes.FIREWORK,
-                    this.getX(),
-                    this.getY(),
-                    this.getZ(),
-                    this.random.nextGaussian() * 0.05,
-                    -this.getDeltaMovement().y * 0.5,
-                    this.random.nextGaussian() * 0.05
-                );
+            this.level().addParticle(ParticleTypes.FIREWORK, this.getX(), this.getY(), this.getZ(), this.random.nextGaussian() * 0.05D, -this.getDeltaMovement().y * 0.5D, this.random.nextGaussian() * 0.05D);
         }
 
         if (!this.level().isClientSide && this.life > this.lifetime) {
-            this.explode();
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
         }
+
     }
 
     private void explode() {
-        this.level().broadcastEntityEvent(this, (byte)17);
+        this.level().broadcastEntityEvent(this, (byte) 17);
         this.gameEvent(GameEvent.EXPLODE, this.getOwner());
         this.dealExplosionDamage();
-        this.discard();
+        this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
     }
 
     @Override
     protected void onHitEntity(EntityHitResult entityHitResult) {
         super.onHitEntity(entityHitResult);
         if (!this.level().isClientSide) {
-            this.explode();
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
         }
     }
 
     @Override
     protected void onHitBlock(BlockHitResult blockHitResult) {
-        BlockPos blockPos = new BlockPos(blockHitResult.getBlockPos());
-        this.level().getBlockState(blockPos).entityInside(this.level(), blockPos, this);
+        BlockPos blockposition = new BlockPos(blockHitResult.getBlockPos());
+
+        this.level().getBlockState(blockposition).entityInside(this.level(), blockposition, this);
         if (!this.level().isClientSide() && this.hasExplosion()) {
-            this.explode();
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
         }
 
         super.onHitBlock(blockHitResult);
@@ -208,53 +232,62 @@
     private void dealExplosionDamage() {
         float f = 0.0F;
         List<FireworkExplosion> list = this.getExplosions();
+
         if (!list.isEmpty()) {
-            f = 5.0F + (float)(list.size() * 2);
+            f = 5.0F + (float) (list.size() * 2);
         }
 
         if (f > 0.0F) {
             if (this.attachedToEntity != null) {
-                this.attachedToEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), 5.0F + (float)(list.size() * 2));
+                this.attachedToEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), 5.0F + (float) (list.size() * 2));
             }
 
-            double d = 5.0;
-            Vec3 vec3 = this.position();
+            double d0 = 5.0D;
+            Vec3 vec3d = this.position();
+            List<LivingEntity> list1 = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0D));
+            Iterator iterator = list1.iterator();
 
-            for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0))) {
-                if (livingEntity != this.attachedToEntity && !(this.distanceToSqr(livingEntity) > 25.0)) {
-                    boolean bl = false;
+            while (iterator.hasNext()) {
+                LivingEntity entityliving = (LivingEntity) iterator.next();
 
-                    for (int i = 0; i < 2; i++) {
-                        Vec3 vec32 = new Vec3(livingEntity.getX(), livingEntity.getY(0.5 * (double)i), livingEntity.getZ());
-                        HitResult hitResult = this.level().clip(new ClipContext(vec3, vec32, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
-                        if (hitResult.getType() == HitResult.Type.MISS) {
-                            bl = true;
+                if (entityliving != this.attachedToEntity && this.distanceToSqr((Entity) entityliving) <= 25.0D) {
+                    boolean flag = false;
+
+                    for (int i = 0; i < 2; ++i) {
+                        Vec3 vec3d1 = new Vec3(entityliving.getX(), entityliving.getY(0.5D * (double) i), entityliving.getZ());
+                        BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
+
+                        if (movingobjectpositionblock.getType() == HitResult.Type.MISS) {
+                            flag = true;
                             break;
                         }
                     }
 
-                    if (bl) {
-                        float g = f * (float)Math.sqrt((5.0 - (double)this.distanceTo(livingEntity)) / 5.0);
-                        livingEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), g);
+                    if (flag) {
+                        float f1 = f * (float) Math.sqrt((5.0D - (double) this.distanceTo(entityliving)) / 5.0D);
+
+                        entityliving.hurt(this.damageSources().fireworks(this, this.getOwner()), f1);
                     }
                 }
             }
         }
+
     }
 
     private boolean isAttachedToEntity() {
-        return this.entityData.get(DATA_ATTACHED_TO_TARGET).isPresent();
+        return ((OptionalInt) this.entityData.get(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET)).isPresent();
     }
 
     public boolean isShotAtAngle() {
-        return this.entityData.get(DATA_SHOT_AT_ANGLE);
+        return (Boolean) this.entityData.get(FireworkRocketEntity.DATA_SHOT_AT_ANGLE);
     }
 
     @Override
     public void handleEntityEvent(byte status) {
         if (status == 17 && this.level().isClientSide) {
-            Vec3 vec3 = this.getDeltaMovement();
-            this.level().createFireworks(this.getX(), this.getY(), this.getZ(), vec3.x, vec3.y, vec3.z, this.getExplosions());
+            Vec3 vec3d = this.getDeltaMovement();
+
+            this.level().createFireworks(this.getX(), this.getY(), this.getZ(), vec3d.x, vec3d.y, vec3d.z, this.getExplosions());
         }
 
         super.handleEntityEvent(status);
@@ -266,7 +299,12 @@
         nbt.putInt("Life", this.life);
         nbt.putInt("LifeTime", this.lifetime);
         nbt.put("FireworksItem", this.getItem().save(this.registryAccess()));
-        nbt.putBoolean("ShotAtAngle", this.entityData.get(DATA_SHOT_AT_ANGLE));
+        nbt.putBoolean("ShotAtAngle", (Boolean) this.entityData.get(FireworkRocketEntity.DATA_SHOT_AT_ANGLE));
+        // Paper start
+        if (this.spawningEntity != null) {
+            nbt.putUUID("SpawningEntity", this.spawningEntity);
+        }
+        // Paper end
     }
 
     @Override
@@ -275,29 +313,31 @@
         this.life = nbt.getInt("Life");
         this.lifetime = nbt.getInt("LifeTime");
         if (nbt.contains("FireworksItem", 10)) {
-            this.entityData
-                .set(
-                    DATA_ID_FIREWORKS_ITEM,
-                    ItemStack.parse(this.registryAccess(), nbt.getCompound("FireworksItem")).orElseGet(FireworkRocketEntity::getDefaultItem)
-                );
+            this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, (ItemStack) ItemStack.parse(this.registryAccess(), nbt.getCompound("FireworksItem")).orElseGet(FireworkRocketEntity::getDefaultItem));
         } else {
-            this.entityData.set(DATA_ID_FIREWORKS_ITEM, getDefaultItem());
+            this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, FireworkRocketEntity.getDefaultItem());
         }
 
         if (nbt.contains("ShotAtAngle")) {
-            this.entityData.set(DATA_SHOT_AT_ANGLE, nbt.getBoolean("ShotAtAngle"));
+            this.entityData.set(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, nbt.getBoolean("ShotAtAngle"));
         }
+        // Paper start
+        if (nbt.hasUUID("SpawningEntity")) {
+            this.spawningEntity = nbt.getUUID("SpawningEntity");
+        }
+        // Paper end
     }
 
     private List<FireworkExplosion> getExplosions() {
-        ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-        Fireworks fireworks = itemStack.get(DataComponents.FIREWORKS);
+        ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+        Fireworks fireworks = (Fireworks) itemstack.get(DataComponents.FIREWORKS);
+
         return fireworks != null ? fireworks.explosions() : List.of();
     }
 
     @Override
     public ItemStack getItem() {
-        return this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+        return (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
     }
 
     @Override
