--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
@@ -1,5 +1,7 @@
 package net.minecraft.world.entity.projectile;
 
+import java.util.Iterator;
+import java.util.List;
 import java.util.OptionalInt;
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
@@ -26,8 +28,10 @@
 import net.minecraft.world.phys.EntityHitResult;
 import net.minecraft.world.phys.HitResult;
 import net.minecraft.world.phys.Vec3;
+import org.bukkit.craftbukkit.v1_19_R2.event.CraftEventFactory; // CraftBukkit
 
 public class FireworkRocketEntity extends Projectile implements ItemSupplier {
+
     public static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK);
     private 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);
@@ -35,6 +39,7 @@
     public int lifetime;
     @Nullable
     public LivingEntity attachedToEntity;
+    public java.util.UUID spawningEntity; // Paper
 
     public FireworkRocketEntity(EntityType<? extends FireworkRocketEntity> type, Level world) {
         super(type, world);
@@ -45,8 +50,9 @@
         this.life = 0;
         this.setPos(x, y, z);
         int i = 1;
+
         if (!stack.isEmpty() && stack.hasTag()) {
-            this.entityData.set(DATA_ID_FIREWORKS_ITEM, stack.copy());
+            this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, stack.copy());
             i += stack.getOrCreateTagElement("Fireworks").getByte("Flight");
         }
 
@@ -61,13 +67,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) {
@@ -75,11 +81,27 @@
         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.v1_19_R2.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
+        }
+        super.inactiveTick();
+    }
+    // Spigot End
+
     @Override
     protected void defineSynchedData() {
-        this.entityData.define(DATA_ID_FIREWORKS_ITEM, ItemStack.EMPTY);
-        this.entityData.define(DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
-        this.entityData.define(DATA_SHOT_AT_ANGLE, false);
+        this.entityData.define(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, ItemStack.EMPTY);
+        this.entityData.define(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
+        this.entityData.define(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, false);
     }
 
     @Override
@@ -95,53 +117,58 @@
     @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.5D;
-                    double e = 0.1D;
-                    Vec3 vec32 = this.attachedToEntity.getDeltaMovement();
-                    this.attachedToEntity.setDeltaMovement(vec32.add(vec3.x * 0.1D + (vec3.x * 1.5D - vec32.x) * 0.5D, vec3.y * 0.1D + (vec3.y * 1.5D - vec32.y) * 0.5D, vec3.z * 0.1D + (vec3.z * 1.5D - vec32.z) * 0.5D));
-                    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.0D : 1.15D;
-                this.setDeltaMovement(this.getDeltaMovement().multiply(f, 1.0D, f).add(0.0D, 0.04D, 0.0D));
+                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.getHitResult(this, this::canHitEntity);
+        HitResult movingobjectposition = ProjectileUtil.getHitResult(this, this::canHitEntity);
+
         if (!this.noPhysics) {
-            this.onHit(hitResult);
+            this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
             this.hasImpulse = true;
         }
 
         this.updateRotation();
         if (this.life == 0 && !this.isSilent()) {
-            this.level.playSound((Player)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;
@@ -150,13 +177,17 @@
         }
 
         if (!this.level.isClientSide && this.life > this.lifetime) {
-            this.explode();
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.v1_19_R2.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();
@@ -166,61 +197,82 @@
     protected void onHitEntity(EntityHitResult entityHitResult) {
         super.onHitEntity(entityHitResult);
         if (!this.level.isClientSide) {
-            this.explode();
+            // CraftBukkit start
+            if (!org.bukkit.craftbukkit.v1_19_R2.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.v1_19_R2.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
+                this.explode();
+            }
+            // CraftBukkit end
         }
 
         super.onHitBlock(blockHitResult);
     }
 
     private boolean hasExplosion() {
-        ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-        CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
-        ListTag listTag = compoundTag != null ? compoundTag.getList("Explosions", 10) : null;
-        return listTag != null && !listTag.isEmpty();
+        ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+        CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
+        ListTag nbttaglist = nbttagcompound != null ? nbttagcompound.getList("Explosions", 10) : null;
+
+        return nbttaglist != null && !nbttaglist.isEmpty();
     }
 
     private void dealExplosionDamage() {
         float f = 0.0F;
-        ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-        CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
-        ListTag listTag = compoundTag != null ? compoundTag.getList("Explosions", 10) : null;
-        if (listTag != null && !listTag.isEmpty()) {
-            f = 5.0F + (float)(listTag.size() * 2);
+        ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+        CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
+        ListTag nbttaglist = nbttagcompound != null ? nbttagcompound.getList("Explosions", 10) : null;
+
+        if (nbttaglist != null && !nbttaglist.isEmpty()) {
+            f = 5.0F + (float) (nbttaglist.size() * 2);
         }
 
         if (f > 0.0F) {
             if (this.attachedToEntity != null) {
-                this.attachedToEntity.hurt(DamageSource.fireworks(this, this.getOwner()), 5.0F + (float)(listTag.size() * 2));
+                CraftEventFactory.entityDamage = this; // CraftBukkit
+                this.attachedToEntity.hurt(DamageSource.fireworks(this, this.getOwner()), 5.0F + (float) (nbttaglist.size() * 2));
+                CraftEventFactory.entityDamage = null; // CraftBukkit
             }
 
-            double d = 5.0D;
-            Vec3 vec3 = this.position();
+            double d0 = 5.0D;
+            Vec3 vec3d = this.position();
+            List<LivingEntity> list = this.level.getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0D));
+            Iterator iterator = list.iterator();
 
-            for(LivingEntity livingEntity : this.level.getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0D))) {
-                if (livingEntity != this.attachedToEntity && !(this.distanceToSqr(livingEntity) > 25.0D)) {
-                    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.5D * (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.0D - (double)this.distanceTo(livingEntity)) / 5.0D);
-                        livingEntity.hurt(DamageSource.fireworks(this, this.getOwner()), g);
+                    if (flag) {
+                        float f1 = f * (float) Math.sqrt((5.0D - (double) this.distanceTo(entityliving)) / 5.0D);
+
+                        CraftEventFactory.entityDamage = this; // CraftBukkit
+                        entityliving.hurt(DamageSource.fireworks(this, this.getOwner()), f1);
+                        CraftEventFactory.entityDamage = null; // CraftBukkit
                     }
                 }
             }
@@ -229,25 +281,26 @@
     }
 
     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) {
             if (!this.hasExplosion()) {
-                for(int i = 0; i < this.random.nextInt(3) + 2; ++i) {
+                for (int i = 0; i < this.random.nextInt(3) + 2; ++i) {
                     this.level.addParticle(ParticleTypes.POOF, this.getX(), this.getY(), this.getZ(), this.random.nextGaussian() * 0.05D, 0.005D, this.random.nextGaussian() * 0.05D);
                 }
             } else {
-                ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-                CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
-                Vec3 vec3 = this.getDeltaMovement();
-                this.level.createFireworks(this.getX(), this.getY(), this.getZ(), vec3.x, vec3.y, vec3.z, compoundTag);
+                ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+                CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
+                Vec3 vec3d = this.getDeltaMovement();
+
+                this.level.createFireworks(this.getX(), this.getY(), this.getZ(), vec3d.x, vec3d.y, vec3d.z, nbttagcompound);
             }
         }
 
@@ -259,12 +312,18 @@
         super.addAdditionalSaveData(nbt);
         nbt.putInt("Life", this.life);
         nbt.putInt("LifeTime", this.lifetime);
-        ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-        if (!itemStack.isEmpty()) {
-            nbt.put("FireworksItem", itemStack.save(new CompoundTag()));
+        ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+
+        if (!itemstack.isEmpty()) {
+            nbt.put("FireworksItem", itemstack.save(new CompoundTag()));
         }
 
-        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
@@ -272,21 +331,27 @@
         super.readAdditionalSaveData(nbt);
         this.life = nbt.getInt("Life");
         this.lifetime = nbt.getInt("LifeTime");
-        ItemStack itemStack = ItemStack.of(nbt.getCompound("FireworksItem"));
-        if (!itemStack.isEmpty()) {
-            this.entityData.set(DATA_ID_FIREWORKS_ITEM, itemStack);
+        ItemStack itemstack = ItemStack.of(nbt.getCompound("FireworksItem"));
+
+        if (!itemstack.isEmpty()) {
+            this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, itemstack);
         }
 
         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
     }
 
     @Override
     public ItemStack getItem() {
-        ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
-        return itemStack.isEmpty() ? new ItemStack(Items.FIREWORK_ROCKET) : itemStack;
+        ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
+
+        return itemstack.isEmpty() ? new ItemStack(Items.FIREWORK_ROCKET) : itemstack;
     }
 
     @Override
