--- a/net/minecraft/world/level/block/BambooStalkBlock.java
+++ b/net/minecraft/world/level/block/BambooStalkBlock.java
@@ -130,9 +_,9 @@
     @Override
     protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
         if (state.getValue(STAGE) == 0) {
-            if (random.nextInt(3) == 0 && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) {
+            if (random.nextFloat() < (level.spigotConfig.bambooModifier / (100.0f * 3)) && level.isEmptyBlock(pos.above()) && level.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution
                 int i = this.getHeightBelowUpToMax(level, pos) + 1;
-                if (i < 16) {
+                if (i < level.paperConfig().maxGrowthHeight.bamboo.max) { // Paper - Configurable cactus/bamboo/reed growth height
                     this.growBamboo(state, level, pos, random, i);
                 }
             }
@@ -168,7 +_,7 @@
     public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
         int heightAboveUpToMax = this.getHeightAboveUpToMax(level, pos);
         int heightBelowUpToMax = this.getHeightBelowUpToMax(level, pos);
-        return heightAboveUpToMax + heightBelowUpToMax + 1 < 16 && level.getBlockState(pos.above(heightAboveUpToMax)).getValue(STAGE) != 1;
+        return heightAboveUpToMax + heightBelowUpToMax + 1 < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.above(heightAboveUpToMax)).getValue(STAGE) != 1; // Paper - Configurable cactus/bamboo/reed growth height
     }
 
     @Override
@@ -186,7 +_,7 @@
         for (int i2 = 0; i2 < i1; i2++) {
             BlockPos blockPos = pos.above(heightAboveUpToMax);
             BlockState blockState = level.getBlockState(blockPos);
-            if (i >= 16 || blockState.getValue(STAGE) == 1 || !level.isEmptyBlock(blockPos.above())) {
+            if (i >= level.paperConfig().maxGrowthHeight.bamboo.max || !blockState.is(Blocks.BAMBOO) || blockState.getValue(BambooStalkBlock.STAGE) == 1 || !level.isEmptyBlock(blockPos.above())) { // CraftBukkit - If the BlockSpreadEvent was cancelled, we have no bamboo here // Paper - Configurable cactus/bamboo/reed growth height
                 return;
             }
 
@@ -206,29 +_,38 @@
         BlockPos blockPos = pos.below(2);
         BlockState blockState1 = level.getBlockState(blockPos);
         BambooLeaves bambooLeaves = BambooLeaves.NONE;
+        boolean shouldUpdateOthers = false; // CraftBukkit
         if (age >= 1) {
             if (!blockState.is(Blocks.BAMBOO) || blockState.getValue(LEAVES) == BambooLeaves.NONE) {
                 bambooLeaves = BambooLeaves.SMALL;
             } else if (blockState.is(Blocks.BAMBOO) && blockState.getValue(LEAVES) != BambooLeaves.NONE) {
                 bambooLeaves = BambooLeaves.LARGE;
                 if (blockState1.is(Blocks.BAMBOO)) {
-                    level.setBlock(pos.below(), blockState.setValue(LEAVES, BambooLeaves.SMALL), 3);
-                    level.setBlock(blockPos, blockState1.setValue(LEAVES, BambooLeaves.NONE), 3);
+                    // CraftBukkit start - moved down
+                    // level.setBlock(pos.below(), blockState.setValue(LEAVES, BambooLeaves.SMALL), 3);
+                    // level.setBlock(blockPos, blockState1.setValue(LEAVES, BambooLeaves.NONE), 3);
+                    shouldUpdateOthers = true;
+                    // CraftBukkit end
                 }
             }
         }
 
         int i = state.getValue(AGE) != 1 && !blockState1.is(Blocks.BAMBOO) ? 0 : 1;
-        int i1 = (age < 11 || !(random.nextFloat() < 0.25F)) && age != 15 ? 0 : 1;
-        level.setBlock(
-            pos.above(), this.defaultBlockState().setValue(AGE, Integer.valueOf(i)).setValue(LEAVES, bambooLeaves).setValue(STAGE, Integer.valueOf(i1)), 3
-        );
+        int i1 = (age < level.paperConfig().maxGrowthHeight.bamboo.min || random.nextFloat() >= 0.25F) && age != (level.paperConfig().maxGrowthHeight.bamboo.max - 1) ? 0 : 1; // Paper - Configurable cactus/bamboo/reed growth height
+        // CraftBukkit start
+        if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(level, pos, pos.above(), this.defaultBlockState().setValue(net.minecraft.world.level.block.BambooStalkBlock.AGE, i).setValue(BambooStalkBlock.LEAVES, bambooLeaves).setValue(net.minecraft.world.level.block.BambooStalkBlock.STAGE, i1), 3)) {
+            if (shouldUpdateOthers) {
+                level.setBlock(pos.below(), blockState.setValue(BambooStalkBlock.LEAVES, BambooLeaves.SMALL), 3);
+                level.setBlock(blockPos, blockState1.setValue(BambooStalkBlock.LEAVES, BambooLeaves.NONE), 3);
+            }
+        }
+        // CraftBukkit end
     }
 
     protected int getHeightAboveUpToMax(BlockGetter level, BlockPos pos) {
         int i = 0;
 
-        while (i < 16 && level.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO)) {
+        while (i < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.above(i + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height
             i++;
         }
 
@@ -238,7 +_,7 @@
     protected int getHeightBelowUpToMax(BlockGetter level, BlockPos pos) {
         int i = 0;
 
-        while (i < 16 && level.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO)) {
+        while (i < ((Level) level).paperConfig().maxGrowthHeight.bamboo.max && level.getBlockState(pos.below(i + 1)).is(Blocks.BAMBOO)) { // Paper - Configurable cactus/bamboo/reed growth height
             i++;
         }
 
