--- a/net/minecraft/world/level/block/CactusBlock.java
+++ b/net/minecraft/world/level/block/CactusBlock.java
@@ -1,5 +1,6 @@
 package net.minecraft.world.level.block;
 
+import java.util.Iterator;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.server.level.ServerLevel;
@@ -19,8 +20,10 @@
 import net.minecraft.world.level.pathfinder.PathComputationType;
 import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory; // CraftBukkit
 
 public class CactusBlock extends Block {
+
     public static final IntegerProperty AGE = BlockStateProperties.AGE_15;
     public static final int MAX_AGE = 15;
     protected static final int AABB_OFFSET = 1;
@@ -29,7 +32,7 @@
 
     protected CactusBlock(BlockBehaviour.Properties settings) {
         super(settings);
-        this.registerDefaultState(this.stateDefinition.any().setValue(AGE, Integer.valueOf(0)));
+        this.registerDefaultState((BlockState) ((BlockState) this.stateDefinition.any()).setValue(CactusBlock.AGE, 0));
     }
 
     @Override
@@ -42,21 +45,27 @@
 
     @Override
     public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
-        BlockPos blockPos = pos.above();
-        if (world.isEmptyBlock(blockPos)) {
+        BlockPos blockposition1 = pos.above();
+
+        if (world.isEmptyBlock(blockposition1)) {
             int i;
-            for(i = 1; world.getBlockState(pos.below(i)).is(this); ++i) {
+
+            for (i = 1; world.getBlockState(pos.below(i)).is((Block) this); ++i) {
+                ;
             }
 
-            if (i < 3) {
-                int j = state.getValue(AGE);
-                if (j == 15) {
-                    world.setBlockAndUpdate(blockPos, this.defaultBlockState());
-                    BlockState blockState = state.setValue(AGE, Integer.valueOf(0));
-                    world.setBlock(pos, blockState, 4);
-                    world.neighborChanged(blockState, blockPos, this, pos, false);
-                } else {
-                    world.setBlock(pos, state.setValue(AGE, Integer.valueOf(j + 1)), 4);
+            if (i < world.paperConfig().maxGrowthHeight.cactus) { // Paper - Configurable growth height
+                int j = (Integer) state.getValue(CactusBlock.AGE);
+
+                int modifier = world.spigotConfig.cactusModifier; // Spigot - SPIGOT-7159: Better modifier resolution
+                if (j >= 15 || (modifier != 100 && random.nextFloat() < (modifier / (100.0f * 16)))) { // Spigot - SPIGOT-7159: Better modifier resolution
+                    CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.defaultBlockState()); // CraftBukkit
+                    BlockState iblockdata1 = (BlockState) state.setValue(CactusBlock.AGE, 0);
+
+                    world.setBlock(pos, iblockdata1, 4);
+                    world.neighborChanged(iblockdata1, blockposition1, this, pos, false);
+                } else if (modifier == 100 || random.nextFloat() < (modifier / (100.0f * 16))) { // Spigot - SPIGOT-7159: Better modifier resolution
+                    world.setBlock(pos, (BlockState) state.setValue(CactusBlock.AGE, j + 1), 4);
                 }
 
             }
@@ -65,18 +74,18 @@
 
     @Override
     public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        return COLLISION_SHAPE;
+        return CactusBlock.COLLISION_SHAPE;
     }
 
     @Override
     public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        return OUTLINE_SHAPE;
+        return CactusBlock.OUTLINE_SHAPE;
     }
 
     @Override
     public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
         if (!state.canSurvive(world, pos)) {
-            world.scheduleTick(pos, this, 1);
+            world.scheduleTick(pos, (Block) this, 1);
         }
 
         return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
@@ -84,25 +93,36 @@
 
     @Override
     public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) {
-        for(Direction direction : Direction.Plane.HORIZONTAL) {
-            BlockState blockState = world.getBlockState(pos.relative(direction));
-            if (blockState.isSolid() || world.getFluidState(pos.relative(direction)).is(FluidTags.LAVA)) {
-                return false;
+        Iterator iterator = Direction.Plane.HORIZONTAL.iterator();
+
+        Direction enumdirection;
+        BlockState iblockdata1;
+
+        do {
+            if (!iterator.hasNext()) {
+                BlockState iblockdata2 = world.getBlockState(pos.below());
+
+                return (iblockdata2.is(Blocks.CACTUS) || iblockdata2.is(BlockTags.SAND)) && !world.getBlockState(pos.above()).liquid();
             }
-        }
 
-        BlockState blockState2 = world.getBlockState(pos.below());
-        return (blockState2.is(Blocks.CACTUS) || blockState2.is(BlockTags.SAND)) && !world.getBlockState(pos.above()).liquid();
+            enumdirection = (Direction) iterator.next();
+            iblockdata1 = world.getBlockState(pos.relative(enumdirection));
+        } while (!iblockdata1.isSolid() && !world.getFluidState(pos.relative(enumdirection)).is(FluidTags.LAVA));
+
+        return false;
     }
 
     @Override
     public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+        if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper
+        CraftEventFactory.blockDamage = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); // CraftBukkit
         entity.hurt(world.damageSources().cactus(), 1.0F);
+        CraftEventFactory.blockDamage = null; // CraftBukkit
     }
 
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
-        builder.add(AGE);
+        builder.add(CactusBlock.AGE);
     }
 
     @Override
