--- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java
@@ -15,7 +15,9 @@
 import net.minecraft.sounds.SoundSource;
 import net.minecraft.util.RandomSource;
 import net.minecraft.world.damagesource.DamageSource;
+import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.level.Level;
 import net.minecraft.world.level.block.SculkCatalystBlock;
 import net.minecraft.world.level.block.SculkSpreader;
@@ -27,6 +29,7 @@
 import net.minecraft.world.phys.Vec3;
 
 public class SculkCatalystBlockEntity extends BlockEntity implements GameEventListener.Provider<SculkCatalystBlockEntity.CatalystListener> {
+
     private final SculkCatalystBlockEntity.CatalystListener catalystListener;
 
     public SculkCatalystBlockEntity(BlockPos pos, BlockState state) {
@@ -34,8 +37,18 @@
         this.catalystListener = new SculkCatalystBlockEntity.CatalystListener(state, new BlockPositionSource(pos));
     }
 
+    // Paper start - Fix NPE in SculkBloomEvent world access
+    @Override
+    public void setLevel(Level level) {
+        super.setLevel(level);
+        this.catalystListener.sculkSpreader.level = level;
+    }
+    // Paper end - Fix NPE in SculkBloomEvent world access
+
     public static void serverTick(Level world, BlockPos pos, BlockState state, SculkCatalystBlockEntity blockEntity) {
+        org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = blockEntity.getBlockPos(); // CraftBukkit - SPIGOT-7068: Add source block override, not the most elegant way but better than passing down a BlockPosition up to five methods deep.
         blockEntity.catalystListener.getSculkSpreader().updateCursors(world, pos, world.getRandom(), true);
+        org.bukkit.craftbukkit.event.CraftEventFactory.sourceBlockOverride = null; // CraftBukkit
     }
 
     @Override
@@ -56,6 +69,7 @@
     }
 
     public static class CatalystListener implements GameEventListener {
+
         public static final int PULSE_TICKS = 8;
         final SculkSpreader sculkSpreader;
         private final BlockState blockState;
@@ -65,6 +79,7 @@
             this.blockState = state;
             this.positionSource = positionSource;
             this.sculkSpreader = SculkSpreader.createLevelSpreader();
+            // this.sculkSpreader.level = this.level; // CraftBukkit // Paper - Fix NPE in SculkBloomEvent world access
         }
 
         @Override
@@ -84,23 +99,32 @@
 
         @Override
         public boolean handleGameEvent(ServerLevel world, Holder<GameEvent> event, GameEvent.Context emitter, Vec3 emitterPos) {
-            if (event.is(GameEvent.ENTITY_DIE) && emitter.sourceEntity() instanceof LivingEntity livingEntity) {
-                if (!livingEntity.wasExperienceConsumed()) {
-                    DamageSource damageSource = livingEntity.getLastDamageSource();
-                    int i = livingEntity.getExperienceReward(world, Optionull.map(damageSource, DamageSource::getEntity));
-                    if (livingEntity.shouldDropExperience() && i > 0) {
-                        this.sculkSpreader.addCursors(BlockPos.containing(emitterPos.relative(Direction.UP, 0.5)), i);
-                        this.tryAwardItSpreadsAdvancement(world, livingEntity);
+            if (event.is((Holder) GameEvent.ENTITY_DIE)) {
+                Entity entity = emitter.sourceEntity();
+
+                if (entity instanceof LivingEntity) {
+                    LivingEntity entityliving = (LivingEntity) entity;
+
+                    if (!entityliving.wasExperienceConsumed()) {
+                        DamageSource damagesource = entityliving.getLastDamageSource();
+                        int i = entityliving.getExperienceReward(world, (Entity) Optionull.map(damagesource, DamageSource::getEntity));
+
+                        if (entityliving.shouldDropExperience() && i > 0) {
+                            this.sculkSpreader.addCursors(BlockPos.containing(emitterPos.relative(Direction.UP, 0.5D)), i);
+                            this.tryAwardItSpreadsAdvancement(world, entityliving);
+                        }
+
+                        entityliving.skipDropExperience();
+                        this.positionSource.getPosition(world).ifPresent((vec3d1) -> {
+                            this.bloom(world, BlockPos.containing(vec3d1), this.blockState, world.getRandom());
+                        });
                     }
 
-                    livingEntity.skipDropExperience();
-                    this.positionSource.getPosition(world).ifPresent(pos -> this.bloom(world, BlockPos.containing(pos), this.blockState, world.getRandom()));
+                    return true;
                 }
-
-                return true;
-            } else {
-                return false;
             }
+
+            return false;
         }
 
         @VisibleForTesting
@@ -109,19 +133,21 @@
         }
 
         public void bloom(ServerLevel world, BlockPos pos, BlockState state, RandomSource random) {
-            world.setBlock(pos, state.setValue(SculkCatalystBlock.PULSE, Boolean.valueOf(true)), 3);
+            world.setBlock(pos, (BlockState) state.setValue(SculkCatalystBlock.PULSE, true), 3);
             world.scheduleTick(pos, state.getBlock(), 8);
-            world.sendParticles(ParticleTypes.SCULK_SOUL, (double)pos.getX() + 0.5, (double)pos.getY() + 1.15, (double)pos.getZ() + 0.5, 2, 0.2, 0.0, 0.2, 0.0);
-            world.playSound(null, pos, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + random.nextFloat() * 0.4F);
+            world.sendParticles(ParticleTypes.SCULK_SOUL, (double) pos.getX() + 0.5D, (double) pos.getY() + 1.15D, (double) pos.getZ() + 0.5D, 2, 0.2D, 0.0D, 0.2D, 0.0D);
+            world.playSound((Player) null, pos, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + random.nextFloat() * 0.4F);
         }
 
         private void tryAwardItSpreadsAdvancement(Level world, LivingEntity deadEntity) {
-            if (deadEntity.getLastHurtByMob() instanceof ServerPlayer serverPlayer) {
-                DamageSource damageSource = deadEntity.getLastDamageSource() == null
-                    ? world.damageSources().playerAttack(serverPlayer)
-                    : deadEntity.getLastDamageSource();
-                CriteriaTriggers.KILL_MOB_NEAR_SCULK_CATALYST.trigger(serverPlayer, deadEntity, damageSource);
+            LivingEntity entityliving1 = deadEntity.getLastHurtByMob();
+
+            if (entityliving1 instanceof ServerPlayer entityplayer) {
+                DamageSource damagesource = deadEntity.getLastDamageSource() == null ? world.damageSources().playerAttack(entityplayer) : deadEntity.getLastDamageSource();
+
+                CriteriaTriggers.KILL_MOB_NEAR_SCULK_CATALYST.trigger(entityplayer, deadEntity, damagesource);
             }
+
         }
     }
 }
