--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
@@ -48,6 +_,7 @@
     public int lifetime = 0;
     @Nullable
     public LivingEntity attachedToEntity;
+    @Nullable public java.util.UUID spawningEntity; // Paper
 
     public FireworkRocketEntity(EntityType<? extends FireworkRocketEntity> entityType, Level level) {
         super(entityType, level);
@@ -163,7 +_,7 @@
         }
 
         if (!this.noPhysics && this.isAlive() && hitResultOnMoveVector.getType() != HitResult.Type.MISS) {
-            this.hitTargetOrDeflectSelf(hitResultOnMoveVector);
+            this.preHitTargetOrDeflectSelf(hitResultOnMoveVector); // CraftBukkit - projectile hit event
             this.hasImpulse = true;
         }
 
@@ -187,7 +_,11 @@
         }
 
         if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) {
-            this.explode(serverLevel);
+            // Paper start - Call FireworkExplodeEvent
+            if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) {
+                this.explode(serverLevel);
+            }
+            // Paper end - Call FireworkExplodeEvent
         }
     }
 
@@ -195,14 +_,18 @@
         level.broadcastEntityEvent(this, (byte)17);
         this.gameEvent(GameEvent.EXPLODE, this.getOwner());
         this.dealExplosionDamage(level);
-        this.discard();
+        this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
     }
 
     @Override
     protected void onHitEntity(EntityHitResult result) {
         super.onHitEntity(result);
         if (this.level() instanceof ServerLevel serverLevel) {
-            this.explode(serverLevel);
+            // Paper start - Call FireworkExplodeEvent
+            if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) {
+                this.explode(serverLevel);
+            }
+            // Paper end - Call FireworkExplodeEvent
         }
     }
 
@@ -211,7 +_,11 @@
         BlockPos blockPos = new BlockPos(result.getBlockPos());
         this.level().getBlockState(blockPos).entityInside(this.level(), blockPos, this, InsideBlockEffectApplier.NOOP);
         if (this.level() instanceof ServerLevel serverLevel && this.hasExplosion()) {
-            this.explode(serverLevel);
+            // Paper start - Call FireworkExplodeEvent
+            if (org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this)) {
+                this.explode(serverLevel);
+            }
+            // Paper end - Call FireworkExplodeEvent
         }
 
         super.onHitBlock(result);
@@ -283,6 +_,7 @@
         output.putInt("LifeTime", this.lifetime);
         output.store("FireworksItem", ItemStack.CODEC, this.getItem());
         output.putBoolean("ShotAtAngle", this.entityData.get(DATA_SHOT_AT_ANGLE));
+        output.storeNullable("SpawningEntity", net.minecraft.core.UUIDUtil.CODEC, this.spawningEntity); // Paper
     }
 
     @Override
@@ -292,6 +_,7 @@
         this.lifetime = input.getIntOr("LifeTime", 0);
         this.entityData.set(DATA_ID_FIREWORKS_ITEM, input.read("FireworksItem", ItemStack.CODEC).orElse(getDefaultItem()));
         this.entityData.set(DATA_SHOT_AT_ANGLE, input.getBooleanOr("ShotAtAngle", false));
+        this.spawningEntity = input.read("SpawningEntity", net.minecraft.core.UUIDUtil.CODEC).orElse(null); // Paper
     }
 
     private List<FireworkExplosion> getExplosions() {
