--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -72,6 +_,7 @@
     public CompoundTag blockData;
     public boolean forceTickAfterTeleportToDuplicate;
     protected static final EntityDataAccessor<BlockPos> DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS);
+    public boolean autoExpire = true; // Paper - Expand FallingBlock API
 
     public FallingBlockEntity(EntityType<? extends FallingBlockEntity> entityType, Level level) {
         super(entityType, level);
@@ -97,6 +_,7 @@
             pos.getZ() + 0.5,
             blockState.hasProperty(BlockStateProperties.WATERLOGGED) ? blockState.setValue(BlockStateProperties.WATERLOGGED, false) : blockState
         );
+        if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(fallingBlockEntity, pos, blockState.getFluidState().createLegacyBlock())) return fallingBlockEntity; // CraftBukkit
         level.setBlock(pos, blockState.getFluidState().createLegacyBlock(), 3);
         level.addFreshEntity(fallingBlockEntity);
         return fallingBlockEntity;
@@ -147,13 +_,22 @@
     @Override
     public void tick() {
         if (this.blockState.isAir()) {
-            this.discard();
+            this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
         } else {
             Block block = this.blockState.getBlock();
             this.time++;
             this.applyGravity();
             this.move(MoverType.SELF, this.getDeltaMovement());
             this.applyEffectsFromBlocks();
+            // Paper start - Configurable falling blocks height nerf
+            if (this.level().paperConfig().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) {
+                if (this.dropItem && this.level() instanceof final ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
+                    this.spawnAtLocation(serverLevel, block);
+                }
+                this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.OUT_OF_WORLD);
+                return;
+            }
+            // Paper end - Configurable falling blocks height nerf
             this.handlePortal();
             if (this.level() instanceof ServerLevel serverLevel && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) {
                 BlockPos blockPos = this.blockPosition();
@@ -174,12 +_,12 @@
                 }
 
                 if (!this.onGround() && !flag1) {
-                    if (this.time > 100 && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || this.time > 600) {
+                    if ((this.time > 100 && autoExpire) && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || (this.time > 600 && autoExpire)) { // Paper - Expand FallingBlock API
                         if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
                             this.spawnAtLocation(serverLevel, block);
                         }
 
-                        this.discard();
+                        this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
                     }
                 } else {
                     BlockState blockState = this.level().getBlockState(blockPos);
@@ -197,12 +_,18 @@
                                     this.blockState = this.blockState.setValue(BlockStateProperties.WATERLOGGED, true);
                                 }
 
+                                // CraftBukkit start
+                                if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, this.blockState)) {
+                                    this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // SPIGOT-6586 called before the event in previous versions
+                                    return;
+                                }
+                                // CraftBukkit end
                                 if (this.level().setBlock(blockPos, this.blockState, 3)) {
                                     ((ServerLevel)this.level())
                                         .getChunkSource()
                                         .chunkMap
                                         .broadcast(this, new ClientboundBlockUpdatePacket(blockPos, this.level().getBlockState(blockPos)));
-                                    this.discard();
+                                    this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                                     if (block instanceof Fallable) {
                                         ((Fallable)block).onLand(this.level(), blockPos, this.blockState, blockState, this);
                                     }
@@ -227,19 +_,19 @@
                                         }
                                     }
                                 } else if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
-                                    this.discard();
+                                    this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
                                     this.callOnBrokenAfterFall(block, blockPos);
                                     this.spawnAtLocation(serverLevel, block);
                                 }
                             } else {
-                                this.discard();
+                                this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
                                 if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
                                     this.callOnBrokenAfterFall(block, blockPos);
                                     this.spawnAtLocation(serverLevel, block);
                                 }
                             }
                         } else {
-                            this.discard();
+                            this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                             this.callOnBrokenAfterFall(block, blockPos);
                         }
                     }
@@ -299,6 +_,7 @@
         }
 
         output.putBoolean("CancelDrop", this.cancelDrop);
+        if (!this.autoExpire) output.putBoolean("Paper.AutoExpire", false); // Paper - Expand FallingBlock API
     }
 
     @Override
@@ -310,8 +_,9 @@
         this.fallDamagePerDistance = input.getFloatOr("FallHurtAmount", 0.0F);
         this.fallDamageMax = input.getIntOr("FallHurtMax", 40);
         this.dropItem = input.getBooleanOr("DropItem", true);
-        this.blockData = input.read("TileEntityData", CompoundTag.CODEC).orElse(null);
+        this.blockData = input.read("TileEntityData", CompoundTag.CODEC).map(blockData -> this.level().paperConfig().entities.spawning.filterBadTileEntityNbtFromFallingBlocks && this.blockState.getBlock() instanceof net.minecraft.world.level.block.GameMasterBlock ? null : blockData).map(CompoundTag::copy).orElse(null); // Paper - Filter bad block entity nbt data from falling blocks
         this.cancelDrop = input.getBooleanOr("CancelDrop", false);
+        this.autoExpire = input.getBooleanOr("Paper.AutoExpire", true); // Paper - Expand FallingBlock API
     }
 
     public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) {
@@ -368,7 +_,7 @@
         ResourceKey<Level> resourceKey1 = this.level().dimension();
         boolean flag = (resourceKey1 == Level.END || resourceKey == Level.END) && resourceKey1 != resourceKey;
         Entity entity = super.teleport(teleportTransition);
-        this.forceTickAfterTeleportToDuplicate = entity != null && flag;
+        this.forceTickAfterTeleportToDuplicate = entity != null && flag && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper
         return entity;
     }
 }
