--- a/net/minecraft/world/level/block/SweetBerryBushBlock.java
+++ b/net/minecraft/world/level/block/SweetBerryBushBlock.java
@@ -2,6 +2,7 @@
 
 import com.mojang.serialization.MapCodec;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
 import net.minecraft.server.level.ServerLevel;
 import net.minecraft.sounds.SoundEvents;
 import net.minecraft.sounds.SoundSource;
@@ -28,23 +29,30 @@
 import net.minecraft.world.phys.Vec3;
 import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import java.util.Collections;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.player.PlayerHarvestBlockEvent;
+// CraftBukkit end
 
 public class SweetBerryBushBlock extends BushBlock implements BonemealableBlock {
+
     public static final MapCodec<SweetBerryBushBlock> CODEC = simpleCodec(SweetBerryBushBlock::new);
     private static final float HURT_SPEED_THRESHOLD = 0.003F;
     public static final int MAX_AGE = 3;
     public static final IntegerProperty AGE = BlockStateProperties.AGE_3;
-    private static final VoxelShape SAPLING_SHAPE = Block.box(3.0, 0.0, 3.0, 13.0, 8.0, 13.0);
-    private static final VoxelShape MID_GROWTH_SHAPE = Block.box(1.0, 0.0, 1.0, 15.0, 16.0, 15.0);
+    private static final VoxelShape SAPLING_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 8.0D, 13.0D);
+    private static final VoxelShape MID_GROWTH_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
 
     @Override
     public MapCodec<SweetBerryBushBlock> codec() {
-        return CODEC;
+        return SweetBerryBushBlock.CODEC;
     }
 
     public SweetBerryBushBlock(BlockBehaviour.Properties settings) {
         super(settings);
-        this.registerDefaultState(this.stateDefinition.any().setValue(AGE, Integer.valueOf(0)));
+        this.registerDefaultState((BlockState) ((BlockState) this.stateDefinition.any()).setValue(SweetBerryBushBlock.AGE, 0));
     }
 
     @Override
@@ -54,64 +62,74 @@
 
     @Override
     protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        if (state.getValue(AGE) == 0) {
-            return SAPLING_SHAPE;
-        } else {
-            return state.getValue(AGE) < 3 ? MID_GROWTH_SHAPE : super.getShape(state, world, pos, context);
-        }
+        return (Integer) state.getValue(SweetBerryBushBlock.AGE) == 0 ? SweetBerryBushBlock.SAPLING_SHAPE : ((Integer) state.getValue(SweetBerryBushBlock.AGE) < 3 ? SweetBerryBushBlock.MID_GROWTH_SHAPE : super.getShape(state, world, pos, context));
     }
 
     @Override
     protected boolean isRandomlyTicking(BlockState state) {
-        return state.getValue(AGE) < 3;
+        return (Integer) state.getValue(SweetBerryBushBlock.AGE) < 3;
     }
 
     @Override
     protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
-        int i = state.getValue(AGE);
-        if (i < 3 && random.nextInt(5) == 0 && world.getRawBrightness(pos.above(), 0) >= 9) {
-            BlockState blockState = state.setValue(AGE, Integer.valueOf(i + 1));
-            world.setBlock(pos, blockState, 2);
-            world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState));
+        int i = (Integer) state.getValue(SweetBerryBushBlock.AGE);
+
+        if (i < 3 && random.nextFloat() < (world.spigotConfig.sweetBerryModifier / (100.0f * 5)) && world.getRawBrightness(pos.above(), 0) >= 9) { // Spigot - SPIGOT-7159: Better modifier resolution
+            BlockState iblockdata1 = (BlockState) state.setValue(SweetBerryBushBlock.AGE, i + 1);
+
+            if (!CraftEventFactory.handleBlockGrowEvent(world, pos, iblockdata1, 2)) return; // CraftBukkit
+            world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(iblockdata1));
         }
+
     }
 
     @Override
     protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) {
+        if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent
         if (entity instanceof LivingEntity && entity.getType() != EntityType.FOX && entity.getType() != EntityType.BEE) {
-            entity.makeStuckInBlock(state, new Vec3(0.8F, 0.75, 0.8F));
-            if (!world.isClientSide && state.getValue(AGE) > 0 && (entity.xOld != entity.getX() || entity.zOld != entity.getZ())) {
-                double d = Math.abs(entity.getX() - entity.xOld);
-                double e = Math.abs(entity.getZ() - entity.zOld);
-                if (d >= 0.003F || e >= 0.003F) {
-                    entity.hurt(world.damageSources().sweetBerryBush(), 1.0F);
+            entity.makeStuckInBlock(state, new Vec3(0.800000011920929D, 0.75D, 0.800000011920929D));
+            if (!world.isClientSide && (Integer) state.getValue(SweetBerryBushBlock.AGE) > 0 && (entity.xOld != entity.getX() || entity.zOld != entity.getZ())) {
+                double d0 = Math.abs(entity.getX() - entity.xOld);
+                double d1 = Math.abs(entity.getZ() - entity.zOld);
+
+                if (d0 >= 0.003000000026077032D || d1 >= 0.003000000026077032D) {
+                    entity.hurt(world.damageSources().sweetBerryBush().directBlock(world, pos), 1.0F); // CraftBukkit
                 }
             }
+
         }
     }
 
     @Override
-    protected ItemInteractionResult useItemOn(
-        ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit
-    ) {
-        int i = state.getValue(AGE);
-        boolean bl = i == 3;
-        return !bl && stack.is(Items.BONE_MEAL)
-            ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
-            : super.useItemOn(stack, state, world, pos, player, hand, hit);
+    protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
+        int i = (Integer) state.getValue(SweetBerryBushBlock.AGE);
+        boolean flag = i == 3;
+
+        return !flag && stack.is(Items.BONE_MEAL) ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION : super.useItemOn(stack, state, world, pos, player, hand, hit);
     }
 
     @Override
     protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
-        int i = state.getValue(AGE);
-        boolean bl = i == 3;
+        int i = (Integer) state.getValue(SweetBerryBushBlock.AGE);
+        boolean flag = i == 3;
+
         if (i > 1) {
             int j = 1 + world.random.nextInt(2);
-            popResource(world, pos, new ItemStack(Items.SWEET_BERRIES, j + (bl ? 1 : 0)));
-            world.playSound(null, pos, SoundEvents.SWEET_BERRY_BUSH_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, 0.8F + world.random.nextFloat() * 0.4F);
-            BlockState blockState = state.setValue(AGE, Integer.valueOf(1));
-            world.setBlock(pos, blockState, 2);
-            world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, blockState));
+
+            // CraftBukkit start - useWithoutItem is always MAIN_HAND
+            PlayerHarvestBlockEvent event = CraftEventFactory.callPlayerHarvestBlockEvent(world, pos, player, InteractionHand.MAIN_HAND, Collections.singletonList(new ItemStack(Items.SWEET_BERRIES, j + (flag ? 1 : 0))));
+            if (event.isCancelled()) {
+                return InteractionResult.SUCCESS; // We need to return a success either way, because making it PASS or FAIL will result in a bug where cancelling while harvesting w/ block in hand places block
+            }
+            for (org.bukkit.inventory.ItemStack itemStack : event.getItemsHarvested()) {
+                popResource(world, pos, CraftItemStack.asNMSCopy(itemStack));
+            }
+            // CraftBukkit end
+            world.playSound((Player) null, pos, SoundEvents.SWEET_BERRY_BUSH_PICK_BERRIES, SoundSource.BLOCKS, 1.0F, 0.8F + world.random.nextFloat() * 0.4F);
+            BlockState iblockdata1 = (BlockState) state.setValue(SweetBerryBushBlock.AGE, 1);
+
+            world.setBlock(pos, iblockdata1, 2);
+            world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, iblockdata1));
             return InteractionResult.sidedSuccess(world.isClientSide);
         } else {
             return super.useWithoutItem(state, world, pos, player, hit);
@@ -120,12 +138,12 @@
 
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
-        builder.add(AGE);
+        builder.add(SweetBerryBushBlock.AGE);
     }
 
     @Override
     public boolean isValidBonemealTarget(LevelReader world, BlockPos pos, BlockState state) {
-        return state.getValue(AGE) < 3;
+        return (Integer) state.getValue(SweetBerryBushBlock.AGE) < 3;
     }
 
     @Override
@@ -135,7 +153,8 @@
 
     @Override
     public void performBonemeal(ServerLevel world, RandomSource random, BlockPos pos, BlockState state) {
-        int i = Math.min(3, state.getValue(AGE) + 1);
-        world.setBlock(pos, state.setValue(AGE, Integer.valueOf(i)), 2);
+        int i = Math.min(3, (Integer) state.getValue(SweetBerryBushBlock.AGE) + 1);
+
+        world.setBlock(pos, (BlockState) state.setValue(SweetBerryBushBlock.AGE, i), 2);
     }
 }
