--- a/net/minecraft/world/level/block/ComposterBlock.java
+++ b/net/minecraft/world/level/block/ComposterBlock.java
@@ -7,6 +7,7 @@
 import net.minecraft.Util;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
+import net.minecraft.core.Holder;
 import net.minecraft.core.particles.ParticleTypes;
 import net.minecraft.server.level.ServerLevel;
 import net.minecraft.sounds.SoundEvents;
@@ -41,203 +42,210 @@
 import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.Shapes;
 import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder;
+import org.bukkit.craftbukkit.util.DummyGeneratorAccess;
+// CraftBukkit end
 
 public class ComposterBlock extends Block implements WorldlyContainerHolder {
+
     public static final MapCodec<ComposterBlock> CODEC = simpleCodec(ComposterBlock::new);
     public static final int READY = 8;
     public static final int MIN_LEVEL = 0;
     public static final int MAX_LEVEL = 7;
     public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_COMPOSTER;
-    public static final Object2FloatMap<ItemLike> COMPOSTABLES = new Object2FloatOpenHashMap<>();
+    public static final Object2FloatMap<ItemLike> COMPOSTABLES = new Object2FloatOpenHashMap();
     private static final int AABB_SIDE_THICKNESS = 2;
     private static final VoxelShape OUTER_SHAPE = Shapes.block();
-    private static final VoxelShape[] SHAPES = Util.make(new VoxelShape[9], shapes -> {
-        for (int i = 0; i < 8; i++) {
-            shapes[i] = Shapes.join(OUTER_SHAPE, Block.box(2.0, (double)Math.max(2, 1 + i * 2), 2.0, 14.0, 16.0, 14.0), BooleanOp.ONLY_FIRST);
+    private static final VoxelShape[] SHAPES = (VoxelShape[]) Util.make(new VoxelShape[9], (avoxelshape) -> {
+        for (int i = 0; i < 8; ++i) {
+            avoxelshape[i] = Shapes.join(ComposterBlock.OUTER_SHAPE, Block.box(2.0D, (double) Math.max(2, 1 + i * 2), 2.0D, 14.0D, 16.0D, 14.0D), BooleanOp.ONLY_FIRST);
         }
 
-        shapes[8] = shapes[7];
+        avoxelshape[8] = avoxelshape[7];
     });
 
     @Override
     public MapCodec<ComposterBlock> codec() {
-        return CODEC;
+        return ComposterBlock.CODEC;
     }
 
     public static void bootStrap() {
-        COMPOSTABLES.defaultReturnValue(-1.0F);
+        ComposterBlock.COMPOSTABLES.defaultReturnValue(-1.0F);
         float f = 0.3F;
-        float g = 0.5F;
-        float h = 0.65F;
-        float i = 0.85F;
-        float j = 1.0F;
-        add(0.3F, Items.JUNGLE_LEAVES);
-        add(0.3F, Items.OAK_LEAVES);
-        add(0.3F, Items.SPRUCE_LEAVES);
-        add(0.3F, Items.DARK_OAK_LEAVES);
-        add(0.3F, Items.ACACIA_LEAVES);
-        add(0.3F, Items.CHERRY_LEAVES);
-        add(0.3F, Items.BIRCH_LEAVES);
-        add(0.3F, Items.AZALEA_LEAVES);
-        add(0.3F, Items.MANGROVE_LEAVES);
-        add(0.3F, Items.OAK_SAPLING);
-        add(0.3F, Items.SPRUCE_SAPLING);
-        add(0.3F, Items.BIRCH_SAPLING);
-        add(0.3F, Items.JUNGLE_SAPLING);
-        add(0.3F, Items.ACACIA_SAPLING);
-        add(0.3F, Items.CHERRY_SAPLING);
-        add(0.3F, Items.DARK_OAK_SAPLING);
-        add(0.3F, Items.MANGROVE_PROPAGULE);
-        add(0.3F, Items.BEETROOT_SEEDS);
-        add(0.3F, Items.DRIED_KELP);
-        add(0.3F, Items.SHORT_GRASS);
-        add(0.3F, Items.KELP);
-        add(0.3F, Items.MELON_SEEDS);
-        add(0.3F, Items.PUMPKIN_SEEDS);
-        add(0.3F, Items.SEAGRASS);
-        add(0.3F, Items.SWEET_BERRIES);
-        add(0.3F, Items.GLOW_BERRIES);
-        add(0.3F, Items.WHEAT_SEEDS);
-        add(0.3F, Items.MOSS_CARPET);
-        add(0.3F, Items.PINK_PETALS);
-        add(0.3F, Items.SMALL_DRIPLEAF);
-        add(0.3F, Items.HANGING_ROOTS);
-        add(0.3F, Items.MANGROVE_ROOTS);
-        add(0.3F, Items.TORCHFLOWER_SEEDS);
-        add(0.3F, Items.PITCHER_POD);
-        add(0.5F, Items.DRIED_KELP_BLOCK);
-        add(0.5F, Items.TALL_GRASS);
-        add(0.5F, Items.FLOWERING_AZALEA_LEAVES);
-        add(0.5F, Items.CACTUS);
-        add(0.5F, Items.SUGAR_CANE);
-        add(0.5F, Items.VINE);
-        add(0.5F, Items.NETHER_SPROUTS);
-        add(0.5F, Items.WEEPING_VINES);
-        add(0.5F, Items.TWISTING_VINES);
-        add(0.5F, Items.MELON_SLICE);
-        add(0.5F, Items.GLOW_LICHEN);
-        add(0.65F, Items.SEA_PICKLE);
-        add(0.65F, Items.LILY_PAD);
-        add(0.65F, Items.PUMPKIN);
-        add(0.65F, Items.CARVED_PUMPKIN);
-        add(0.65F, Items.MELON);
-        add(0.65F, Items.APPLE);
-        add(0.65F, Items.BEETROOT);
-        add(0.65F, Items.CARROT);
-        add(0.65F, Items.COCOA_BEANS);
-        add(0.65F, Items.POTATO);
-        add(0.65F, Items.WHEAT);
-        add(0.65F, Items.BROWN_MUSHROOM);
-        add(0.65F, Items.RED_MUSHROOM);
-        add(0.65F, Items.MUSHROOM_STEM);
-        add(0.65F, Items.CRIMSON_FUNGUS);
-        add(0.65F, Items.WARPED_FUNGUS);
-        add(0.65F, Items.NETHER_WART);
-        add(0.65F, Items.CRIMSON_ROOTS);
-        add(0.65F, Items.WARPED_ROOTS);
-        add(0.65F, Items.SHROOMLIGHT);
-        add(0.65F, Items.DANDELION);
-        add(0.65F, Items.POPPY);
-        add(0.65F, Items.BLUE_ORCHID);
-        add(0.65F, Items.ALLIUM);
-        add(0.65F, Items.AZURE_BLUET);
-        add(0.65F, Items.RED_TULIP);
-        add(0.65F, Items.ORANGE_TULIP);
-        add(0.65F, Items.WHITE_TULIP);
-        add(0.65F, Items.PINK_TULIP);
-        add(0.65F, Items.OXEYE_DAISY);
-        add(0.65F, Items.CORNFLOWER);
-        add(0.65F, Items.LILY_OF_THE_VALLEY);
-        add(0.65F, Items.WITHER_ROSE);
-        add(0.65F, Items.FERN);
-        add(0.65F, Items.SUNFLOWER);
-        add(0.65F, Items.LILAC);
-        add(0.65F, Items.ROSE_BUSH);
-        add(0.65F, Items.PEONY);
-        add(0.65F, Items.LARGE_FERN);
-        add(0.65F, Items.SPORE_BLOSSOM);
-        add(0.65F, Items.AZALEA);
-        add(0.65F, Items.MOSS_BLOCK);
-        add(0.65F, Items.BIG_DRIPLEAF);
-        add(0.85F, Items.HAY_BLOCK);
-        add(0.85F, Items.BROWN_MUSHROOM_BLOCK);
-        add(0.85F, Items.RED_MUSHROOM_BLOCK);
-        add(0.85F, Items.NETHER_WART_BLOCK);
-        add(0.85F, Items.WARPED_WART_BLOCK);
-        add(0.85F, Items.FLOWERING_AZALEA);
-        add(0.85F, Items.BREAD);
-        add(0.85F, Items.BAKED_POTATO);
-        add(0.85F, Items.COOKIE);
-        add(0.85F, Items.TORCHFLOWER);
-        add(0.85F, Items.PITCHER_PLANT);
-        add(1.0F, Items.CAKE);
-        add(1.0F, Items.PUMPKIN_PIE);
+        float f1 = 0.5F;
+        float f2 = 0.65F;
+        float f3 = 0.85F;
+        float f4 = 1.0F;
+
+        ComposterBlock.add(0.3F, Items.JUNGLE_LEAVES);
+        ComposterBlock.add(0.3F, Items.OAK_LEAVES);
+        ComposterBlock.add(0.3F, Items.SPRUCE_LEAVES);
+        ComposterBlock.add(0.3F, Items.DARK_OAK_LEAVES);
+        ComposterBlock.add(0.3F, Items.ACACIA_LEAVES);
+        ComposterBlock.add(0.3F, Items.CHERRY_LEAVES);
+        ComposterBlock.add(0.3F, Items.BIRCH_LEAVES);
+        ComposterBlock.add(0.3F, Items.AZALEA_LEAVES);
+        ComposterBlock.add(0.3F, Items.MANGROVE_LEAVES);
+        ComposterBlock.add(0.3F, Items.OAK_SAPLING);
+        ComposterBlock.add(0.3F, Items.SPRUCE_SAPLING);
+        ComposterBlock.add(0.3F, Items.BIRCH_SAPLING);
+        ComposterBlock.add(0.3F, Items.JUNGLE_SAPLING);
+        ComposterBlock.add(0.3F, Items.ACACIA_SAPLING);
+        ComposterBlock.add(0.3F, Items.CHERRY_SAPLING);
+        ComposterBlock.add(0.3F, Items.DARK_OAK_SAPLING);
+        ComposterBlock.add(0.3F, Items.MANGROVE_PROPAGULE);
+        ComposterBlock.add(0.3F, Items.BEETROOT_SEEDS);
+        ComposterBlock.add(0.3F, Items.DRIED_KELP);
+        ComposterBlock.add(0.3F, Items.SHORT_GRASS);
+        ComposterBlock.add(0.3F, Items.KELP);
+        ComposterBlock.add(0.3F, Items.MELON_SEEDS);
+        ComposterBlock.add(0.3F, Items.PUMPKIN_SEEDS);
+        ComposterBlock.add(0.3F, Items.SEAGRASS);
+        ComposterBlock.add(0.3F, Items.SWEET_BERRIES);
+        ComposterBlock.add(0.3F, Items.GLOW_BERRIES);
+        ComposterBlock.add(0.3F, Items.WHEAT_SEEDS);
+        ComposterBlock.add(0.3F, Items.MOSS_CARPET);
+        ComposterBlock.add(0.3F, Items.PINK_PETALS);
+        ComposterBlock.add(0.3F, Items.SMALL_DRIPLEAF);
+        ComposterBlock.add(0.3F, Items.HANGING_ROOTS);
+        ComposterBlock.add(0.3F, Items.MANGROVE_ROOTS);
+        ComposterBlock.add(0.3F, Items.TORCHFLOWER_SEEDS);
+        ComposterBlock.add(0.3F, Items.PITCHER_POD);
+        ComposterBlock.add(0.5F, Items.DRIED_KELP_BLOCK);
+        ComposterBlock.add(0.5F, Items.TALL_GRASS);
+        ComposterBlock.add(0.5F, Items.FLOWERING_AZALEA_LEAVES);
+        ComposterBlock.add(0.5F, Items.CACTUS);
+        ComposterBlock.add(0.5F, Items.SUGAR_CANE);
+        ComposterBlock.add(0.5F, Items.VINE);
+        ComposterBlock.add(0.5F, Items.NETHER_SPROUTS);
+        ComposterBlock.add(0.5F, Items.WEEPING_VINES);
+        ComposterBlock.add(0.5F, Items.TWISTING_VINES);
+        ComposterBlock.add(0.5F, Items.MELON_SLICE);
+        ComposterBlock.add(0.5F, Items.GLOW_LICHEN);
+        ComposterBlock.add(0.65F, Items.SEA_PICKLE);
+        ComposterBlock.add(0.65F, Items.LILY_PAD);
+        ComposterBlock.add(0.65F, Items.PUMPKIN);
+        ComposterBlock.add(0.65F, Items.CARVED_PUMPKIN);
+        ComposterBlock.add(0.65F, Items.MELON);
+        ComposterBlock.add(0.65F, Items.APPLE);
+        ComposterBlock.add(0.65F, Items.BEETROOT);
+        ComposterBlock.add(0.65F, Items.CARROT);
+        ComposterBlock.add(0.65F, Items.COCOA_BEANS);
+        ComposterBlock.add(0.65F, Items.POTATO);
+        ComposterBlock.add(0.65F, Items.WHEAT);
+        ComposterBlock.add(0.65F, Items.BROWN_MUSHROOM);
+        ComposterBlock.add(0.65F, Items.RED_MUSHROOM);
+        ComposterBlock.add(0.65F, Items.MUSHROOM_STEM);
+        ComposterBlock.add(0.65F, Items.CRIMSON_FUNGUS);
+        ComposterBlock.add(0.65F, Items.WARPED_FUNGUS);
+        ComposterBlock.add(0.65F, Items.NETHER_WART);
+        ComposterBlock.add(0.65F, Items.CRIMSON_ROOTS);
+        ComposterBlock.add(0.65F, Items.WARPED_ROOTS);
+        ComposterBlock.add(0.65F, Items.SHROOMLIGHT);
+        ComposterBlock.add(0.65F, Items.DANDELION);
+        ComposterBlock.add(0.65F, Items.POPPY);
+        ComposterBlock.add(0.65F, Items.BLUE_ORCHID);
+        ComposterBlock.add(0.65F, Items.ALLIUM);
+        ComposterBlock.add(0.65F, Items.AZURE_BLUET);
+        ComposterBlock.add(0.65F, Items.RED_TULIP);
+        ComposterBlock.add(0.65F, Items.ORANGE_TULIP);
+        ComposterBlock.add(0.65F, Items.WHITE_TULIP);
+        ComposterBlock.add(0.65F, Items.PINK_TULIP);
+        ComposterBlock.add(0.65F, Items.OXEYE_DAISY);
+        ComposterBlock.add(0.65F, Items.CORNFLOWER);
+        ComposterBlock.add(0.65F, Items.LILY_OF_THE_VALLEY);
+        ComposterBlock.add(0.65F, Items.WITHER_ROSE);
+        ComposterBlock.add(0.65F, Items.FERN);
+        ComposterBlock.add(0.65F, Items.SUNFLOWER);
+        ComposterBlock.add(0.65F, Items.LILAC);
+        ComposterBlock.add(0.65F, Items.ROSE_BUSH);
+        ComposterBlock.add(0.65F, Items.PEONY);
+        ComposterBlock.add(0.65F, Items.LARGE_FERN);
+        ComposterBlock.add(0.65F, Items.SPORE_BLOSSOM);
+        ComposterBlock.add(0.65F, Items.AZALEA);
+        ComposterBlock.add(0.65F, Items.MOSS_BLOCK);
+        ComposterBlock.add(0.65F, Items.BIG_DRIPLEAF);
+        ComposterBlock.add(0.85F, Items.HAY_BLOCK);
+        ComposterBlock.add(0.85F, Items.BROWN_MUSHROOM_BLOCK);
+        ComposterBlock.add(0.85F, Items.RED_MUSHROOM_BLOCK);
+        ComposterBlock.add(0.85F, Items.NETHER_WART_BLOCK);
+        ComposterBlock.add(0.85F, Items.WARPED_WART_BLOCK);
+        ComposterBlock.add(0.85F, Items.FLOWERING_AZALEA);
+        ComposterBlock.add(0.85F, Items.BREAD);
+        ComposterBlock.add(0.85F, Items.BAKED_POTATO);
+        ComposterBlock.add(0.85F, Items.COOKIE);
+        ComposterBlock.add(0.85F, Items.TORCHFLOWER);
+        ComposterBlock.add(0.85F, Items.PITCHER_PLANT);
+        ComposterBlock.add(1.0F, Items.CAKE);
+        ComposterBlock.add(1.0F, Items.PUMPKIN_PIE);
     }
 
     private static void add(float levelIncreaseChance, ItemLike item) {
-        COMPOSTABLES.put(item.asItem(), levelIncreaseChance);
+        ComposterBlock.COMPOSTABLES.put(item.asItem(), levelIncreaseChance);
     }
 
     public ComposterBlock(BlockBehaviour.Properties settings) {
         super(settings);
-        this.registerDefaultState(this.stateDefinition.any().setValue(LEVEL, Integer.valueOf(0)));
+        this.registerDefaultState((BlockState) ((BlockState) this.stateDefinition.any()).setValue(ComposterBlock.LEVEL, 0));
     }
 
     public static void handleFill(Level world, BlockPos pos, boolean fill) {
-        BlockState blockState = world.getBlockState(pos);
+        BlockState iblockdata = world.getBlockState(pos);
+
         world.playLocalSound(pos, fill ? SoundEvents.COMPOSTER_FILL_SUCCESS : SoundEvents.COMPOSTER_FILL, SoundSource.BLOCKS, 1.0F, 1.0F, false);
-        double d = blockState.getShape(world, pos).max(Direction.Axis.Y, 0.5, 0.5) + 0.03125;
-        double e = 0.13125F;
-        double f = 0.7375F;
-        RandomSource randomSource = world.getRandom();
+        double d0 = iblockdata.getShape(world, pos).max(Direction.Axis.Y, 0.5D, 0.5D) + 0.03125D;
+        double d1 = 0.13124999403953552D;
+        double d2 = 0.737500011920929D;
+        RandomSource randomsource = world.getRandom();
 
-        for (int i = 0; i < 10; i++) {
-            double g = randomSource.nextGaussian() * 0.02;
-            double h = randomSource.nextGaussian() * 0.02;
-            double j = randomSource.nextGaussian() * 0.02;
-            world.addParticle(
-                ParticleTypes.COMPOSTER,
-                (double)pos.getX() + 0.13125F + 0.7375F * (double)randomSource.nextFloat(),
-                (double)pos.getY() + d + (double)randomSource.nextFloat() * (1.0 - d),
-                (double)pos.getZ() + 0.13125F + 0.7375F * (double)randomSource.nextFloat(),
-                g,
-                h,
-                j
-            );
+        for (int i = 0; i < 10; ++i) {
+            double d3 = randomsource.nextGaussian() * 0.02D;
+            double d4 = randomsource.nextGaussian() * 0.02D;
+            double d5 = randomsource.nextGaussian() * 0.02D;
+
+            world.addParticle(ParticleTypes.COMPOSTER, (double) pos.getX() + 0.13124999403953552D + 0.737500011920929D * (double) randomsource.nextFloat(), (double) pos.getY() + d0 + (double) randomsource.nextFloat() * (1.0D - d0), (double) pos.getZ() + 0.13124999403953552D + 0.737500011920929D * (double) randomsource.nextFloat(), d3, d4, d5);
         }
+
     }
 
     @Override
     protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        return SHAPES[state.getValue(LEVEL)];
+        return ComposterBlock.SHAPES[(Integer) state.getValue(ComposterBlock.LEVEL)];
     }
 
     @Override
     protected VoxelShape getInteractionShape(BlockState state, BlockGetter world, BlockPos pos) {
-        return OUTER_SHAPE;
+        return ComposterBlock.OUTER_SHAPE;
     }
 
     @Override
     protected VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        return SHAPES[0];
+        return ComposterBlock.SHAPES[0];
     }
 
     @Override
     protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
-        if (state.getValue(LEVEL) == 7) {
+        if ((Integer) state.getValue(ComposterBlock.LEVEL) == 7) {
             world.scheduleTick(pos, state.getBlock(), 20);
         }
+
     }
 
     @Override
-    protected ItemInteractionResult useItemOn(
-        ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit
-    ) {
-        int i = state.getValue(LEVEL);
-        if (i < 8 && COMPOSTABLES.containsKey(stack.getItem())) {
+    protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
+        int i = (Integer) state.getValue(ComposterBlock.LEVEL);
+
+        if (i < 8 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) {
             if (i < 7 && !world.isClientSide) {
-                BlockState blockState = addItem(player, state, world, pos, stack);
-                world.levelEvent(1500, pos, state != blockState ? 1 : 0);
+                BlockState iblockdata1 = ComposterBlock.addItem(player, state, world, pos, stack);
+                // Paper start - handle cancelled events
+                if (iblockdata1 == null) {
+                    return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION;
+                }
+                // Paper end
+
+                world.levelEvent(1500, pos, state != iblockdata1 ? 1 : 0);
                 player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
                 stack.consume(1, player);
             }
@@ -250,9 +258,10 @@
 
     @Override
     protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
-        int i = state.getValue(LEVEL);
+        int i = (Integer) state.getValue(ComposterBlock.LEVEL);
+
         if (i == 8) {
-            extractProduce(player, state, world, pos);
+            ComposterBlock.extractProduce(player, state, world, pos);
             return InteractionResult.sidedSuccess(world.isClientSide);
         } else {
             return InteractionResult.PASS;
@@ -260,60 +269,115 @@
     }
 
     public static BlockState insertItem(Entity user, BlockState state, ServerLevel world, ItemStack stack, BlockPos pos) {
-        int i = state.getValue(LEVEL);
-        if (i < 7 && COMPOSTABLES.containsKey(stack.getItem())) {
-            BlockState blockState = addItem(user, state, world, pos, stack);
+        int i = (Integer) state.getValue(ComposterBlock.LEVEL);
+
+        if (i < 7 && ComposterBlock.COMPOSTABLES.containsKey(stack.getItem())) {
+            // CraftBukkit start
+            double rand = world.getRandom().nextDouble();
+            BlockState iblockdata1 = null; // Paper
+            if (false && (state == iblockdata1 || !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(user, pos, iblockdata1))) { // Paper - move event call into addItem
+                return state;
+            }
+            iblockdata1 = ComposterBlock.addItem(user, state, world, pos, stack, rand);
+            // Paper start - handle cancelled events
+            if (iblockdata1 == null) {
+                return state;
+            }
+            // Paper end
+            // CraftBukkit end
+
             stack.shrink(1);
-            return blockState;
+            return iblockdata1;
         } else {
             return state;
         }
     }
 
     public static BlockState extractProduce(Entity user, BlockState state, Level world, BlockPos pos) {
+        // CraftBukkit start
+        if (user != null && !(user instanceof Player)) {
+            BlockState iblockdata1 = ComposterBlock.empty(user, state, DummyGeneratorAccess.INSTANCE, pos);
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(user, pos, iblockdata1)) {
+                return state;
+            }
+        }
+        // CraftBukkit end
         if (!world.isClientSide) {
-            Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1.01, 0.5).offsetRandom(world.random, 0.7F);
-            ItemEntity itemEntity = new ItemEntity(world, vec3.x(), vec3.y(), vec3.z(), new ItemStack(Items.BONE_MEAL));
-            itemEntity.setDefaultPickUpDelay();
-            world.addFreshEntity(itemEntity);
+            Vec3 vec3d = Vec3.atLowerCornerWithOffset(pos, 0.5D, 1.01D, 0.5D).offsetRandom(world.random, 0.7F);
+            ItemEntity entityitem = new ItemEntity(world, vec3d.x(), vec3d.y(), vec3d.z(), new ItemStack(Items.BONE_MEAL));
+
+            entityitem.setDefaultPickUpDelay();
+            world.addFreshEntity(entityitem);
         }
 
-        BlockState blockState = empty(user, state, world, pos);
-        world.playSound(null, pos, SoundEvents.COMPOSTER_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
-        return blockState;
+        BlockState iblockdata1 = ComposterBlock.empty(user, state, world, pos);
+
+        world.playSound((Player) null, pos, SoundEvents.COMPOSTER_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
+        return iblockdata1;
     }
 
     static BlockState empty(@Nullable Entity user, BlockState state, LevelAccessor world, BlockPos pos) {
-        BlockState blockState = state.setValue(LEVEL, Integer.valueOf(0));
-        world.setBlock(pos, blockState, 3);
-        world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(user, blockState));
-        return blockState;
+        BlockState iblockdata1 = (BlockState) state.setValue(ComposterBlock.LEVEL, 0);
+
+        world.setBlock(pos, iblockdata1, 3);
+        world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(user, iblockdata1));
+        return iblockdata1;
     }
 
+    @Nullable // Paper
     static BlockState addItem(@Nullable Entity user, BlockState state, LevelAccessor world, BlockPos pos, ItemStack stack) {
-        int i = state.getValue(LEVEL);
-        float f = COMPOSTABLES.getFloat(stack.getItem());
-        if ((i != 0 || !(f > 0.0F)) && !(world.getRandom().nextDouble() < (double)f)) {
-            return state;
+        // CraftBukkit start
+        return ComposterBlock.addItem(user, state, world, pos, stack, world.getRandom().nextDouble());
+    }
+
+    @Nullable // Paper - make it nullable
+    static BlockState addItem(@Nullable Entity entity, BlockState iblockdata, LevelAccessor generatoraccess, BlockPos blockposition, ItemStack itemstack, double rand) {
+        // CraftBukkit end
+        int i = (Integer) iblockdata.getValue(ComposterBlock.LEVEL);
+        float f = ComposterBlock.COMPOSTABLES.getFloat(itemstack.getItem());
+
+        // Paper start - Add CompostItemEvent and EntityCompostItemEvent
+        boolean willRaiseLevel = !((i != 0 || f <= 0.0F) && rand >= (double) f);
+        final io.papermc.paper.event.block.CompostItemEvent event;
+        if (entity == null) {
+            event = new io.papermc.paper.event.block.CompostItemEvent(org.bukkit.craftbukkit.block.CraftBlock.at(generatoraccess, blockposition), itemstack.getBukkitStack(), willRaiseLevel);
+        } else {
+            event = new io.papermc.paper.event.entity.EntityCompostItemEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(generatoraccess, blockposition), itemstack.getBukkitStack(), willRaiseLevel);
+        }
+        if (!event.callEvent()) { // check for cancellation of entity event (non entity event can't be cancelled cause of hoppers)
+            return null;
+        }
+        willRaiseLevel = event.willRaiseLevel();
+
+        if (!willRaiseLevel) {
+            // Paper end - Add CompostItemEvent and EntityCompostItemEvent
+            return iblockdata;
         } else {
             int j = i + 1;
-            BlockState blockState = state.setValue(LEVEL, Integer.valueOf(j));
-            world.setBlock(pos, blockState, 3);
-            world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(user, blockState));
+            BlockState iblockdata1 = (BlockState) iblockdata.setValue(ComposterBlock.LEVEL, j);
+            // Paper start - move the EntityChangeBlockEvent here to avoid conflict later for the compost events
+            if (entity != null && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, iblockdata1)) {
+                return null;
+            }
+            // Paper end
+
+            generatoraccess.setBlock(blockposition, iblockdata1, 3);
+            generatoraccess.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, iblockdata1));
             if (j == 7) {
-                world.scheduleTick(pos, state.getBlock(), 20);
+                generatoraccess.scheduleTick(blockposition, iblockdata.getBlock(), 20);
             }
 
-            return blockState;
+            return iblockdata1;
         }
     }
 
     @Override
     protected void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
-        if (state.getValue(LEVEL) == 7) {
-            world.setBlock(pos, state.cycle(LEVEL), 3);
-            world.playSound(null, pos, SoundEvents.COMPOSTER_READY, SoundSource.BLOCKS, 1.0F, 1.0F);
+        if ((Integer) state.getValue(ComposterBlock.LEVEL) == 7) {
+            world.setBlock(pos, (BlockState) state.cycle(ComposterBlock.LEVEL), 3);
+            world.playSound((Player) null, pos, SoundEvents.COMPOSTER_READY, SoundSource.BLOCKS, 1.0F, 1.0F);
         }
+
     }
 
     @Override
@@ -323,12 +387,12 @@
 
     @Override
     protected int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) {
-        return state.getValue(LEVEL);
+        return (Integer) state.getValue(ComposterBlock.LEVEL);
     }
 
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
-        builder.add(LEVEL);
+        builder.add(ComposterBlock.LEVEL);
     }
 
     @Override
@@ -338,22 +402,35 @@
 
     @Override
     public WorldlyContainer getContainer(BlockState state, LevelAccessor world, BlockPos pos) {
-        int i = state.getValue(LEVEL);
-        if (i == 8) {
-            return new ComposterBlock.OutputContainer(state, world, pos, new ItemStack(Items.BONE_MEAL));
-        } else {
-            return (WorldlyContainer)(i < 7 ? new ComposterBlock.InputContainer(state, world, pos) : new ComposterBlock.EmptyContainer());
-        }
+        int i = (Integer) state.getValue(ComposterBlock.LEVEL);
+
+        // CraftBukkit - empty generatoraccess, blockposition
+        return (WorldlyContainer) (i == 8 ? new ComposterBlock.OutputContainer(state, world, pos, new ItemStack(Items.BONE_MEAL)) : (i < 7 ? new ComposterBlock.InputContainer(state, world, pos) : new ComposterBlock.EmptyContainer(world, pos)));
     }
 
-    public static class EmptyContainer extends SimpleContainer implements WorldlyContainer {
-        public EmptyContainer() {
-            super(0);
+    public static class OutputContainer extends SimpleContainer implements WorldlyContainer {
+
+        private final BlockState state;
+        private final LevelAccessor level;
+        private final BlockPos pos;
+        private boolean changed;
+
+        public OutputContainer(BlockState state, LevelAccessor world, BlockPos pos, ItemStack outputItem) {
+            super(outputItem);
+            this.state = state;
+            this.level = world;
+            this.pos = pos;
+            this.bukkitOwner = new CraftBlockInventoryHolder(world, pos, this); // CraftBukkit
+        }
+
+        @Override
+        public int getMaxStackSize() {
+            return 1;
         }
 
         @Override
         public int[] getSlotsForFace(Direction side) {
-            return new int[0];
+            return side == Direction.DOWN ? new int[]{0} : new int[0];
         }
 
         @Override
@@ -363,11 +440,25 @@
 
         @Override
         public boolean canTakeItemThroughFace(int slot, ItemStack stack, Direction dir) {
-            return false;
+            return !this.changed && dir == Direction.DOWN && stack.is(Items.BONE_MEAL);
+        }
+
+        @Override
+        public void setChanged() {
+            // CraftBukkit start - allow putting items back (eg cancelled InventoryMoveItemEvent)
+            if (this.isEmpty()) {
+            ComposterBlock.empty((Entity) null, this.state, this.level, this.pos);
+            this.changed = true;
+            } else {
+                this.level.setBlock(this.pos, this.state, 3);
+                this.changed = false;
+            }
+            // CraftBukkit end
         }
     }
 
     public static class InputContainer extends SimpleContainer implements WorldlyContainer {
+
         private final BlockState state;
         private final LevelAccessor level;
         private final BlockPos pos;
@@ -375,6 +466,7 @@
 
         public InputContainer(BlockState state, LevelAccessor world, BlockPos pos) {
             super(1);
+            this.bukkitOwner = new CraftBlockInventoryHolder(world, pos, this); // CraftBukkit
             this.state = state;
             this.level = world;
             this.pos = pos;
@@ -402,37 +494,34 @@
 
         @Override
         public void setChanged() {
-            ItemStack itemStack = this.getItem(0);
-            if (!itemStack.isEmpty()) {
+            ItemStack itemstack = this.getItem(0);
+
+            if (!itemstack.isEmpty()) {
                 this.changed = true;
-                BlockState blockState = ComposterBlock.addItem(null, this.state, this.level, this.pos, itemStack);
-                this.level.levelEvent(1500, this.pos, blockState != this.state ? 1 : 0);
+                BlockState iblockdata = ComposterBlock.addItem((Entity) null, this.state, this.level, this.pos, itemstack);
+                // Paper start - Add CompostItemEvent and EntityCompostItemEvent
+                if (iblockdata == null) {
+                    return;
+                }
+                // Paper end - Add CompostItemEvent and EntityCompostItemEvent
+
+                this.level.levelEvent(1500, this.pos, iblockdata != this.state ? 1 : 0);
                 this.removeItemNoUpdate(0);
             }
+
         }
     }
 
-    public static class OutputContainer extends SimpleContainer implements WorldlyContainer {
-        private final BlockState state;
-        private final LevelAccessor level;
-        private final BlockPos pos;
-        private boolean changed;
-
-        public OutputContainer(BlockState state, LevelAccessor world, BlockPos pos, ItemStack outputItem) {
-            super(outputItem);
-            this.state = state;
-            this.level = world;
-            this.pos = pos;
-        }
+    public static class EmptyContainer extends SimpleContainer implements WorldlyContainer {
 
-        @Override
-        public int getMaxStackSize() {
-            return 1;
+        public EmptyContainer(LevelAccessor generatoraccess, BlockPos blockposition) { // CraftBukkit
+            super(0);
+            this.bukkitOwner = new CraftBlockInventoryHolder(generatoraccess, blockposition, this); // CraftBukkit
         }
 
         @Override
         public int[] getSlotsForFace(Direction side) {
-            return side == Direction.DOWN ? new int[]{0} : new int[0];
+            return new int[0];
         }
 
         @Override
@@ -442,13 +531,7 @@
 
         @Override
         public boolean canTakeItemThroughFace(int slot, ItemStack stack, Direction dir) {
-            return !this.changed && dir == Direction.DOWN && stack.is(Items.BONE_MEAL);
-        }
-
-        @Override
-        public void setChanged() {
-            ComposterBlock.empty(null, this.state, this.level, this.pos);
-            this.changed = true;
+            return false;
         }
     }
 }
