--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -5,13 +5,14 @@
 import javax.annotation.Nullable;
 import net.minecraft.advancements.CriteriaTriggers;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
 import net.minecraft.core.component.DataComponents;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.network.chat.Component;
 import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
 import net.minecraft.server.level.ServerPlayer;
 import net.minecraft.sounds.SoundEvent;
-import net.minecraft.sounds.SoundSource;
 import net.minecraft.world.InteractionResult;
 import net.minecraft.world.entity.item.ItemEntity;
 import net.minecraft.world.entity.player.Player;
@@ -30,8 +31,14 @@
 import net.minecraft.world.level.block.state.BlockState;
 import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.phys.shapes.CollisionContext;
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
+import org.bukkit.event.block.BlockCanBuildEvent;
+// CraftBukkit end
 
 public class BlockItem extends Item {
+
+    /** @deprecated */
     @Deprecated
     private final Block block;
 
@@ -42,12 +49,14 @@
 
     @Override
     public InteractionResult useOn(UseOnContext context) {
-        InteractionResult interactionResult = this.place(new BlockPlaceContext(context));
-        if (!interactionResult.consumesAction() && context.getItemInHand().has(DataComponents.FOOD)) {
-            InteractionResult interactionResult2 = super.use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
-            return interactionResult2 == InteractionResult.CONSUME ? InteractionResult.CONSUME_PARTIAL : interactionResult2;
+        InteractionResult enuminteractionresult = this.place(new BlockPlaceContext(context));
+
+        if (!enuminteractionresult.consumesAction() && context.getItemInHand().has(DataComponents.FOOD)) {
+            InteractionResult enuminteractionresult1 = super.use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
+
+            return enuminteractionresult1 == InteractionResult.CONSUME ? InteractionResult.CONSUME_PARTIAL : enuminteractionresult1;
         } else {
-            return interactionResult;
+            return enuminteractionresult;
         }
     }
 
@@ -57,43 +66,72 @@
         } else if (!context.canPlace()) {
             return InteractionResult.FAIL;
         } else {
-            BlockPlaceContext blockPlaceContext = this.updatePlacementContext(context);
-            if (blockPlaceContext == null) {
+            BlockPlaceContext blockactioncontext1 = this.updatePlacementContext(context);
+
+            if (blockactioncontext1 == null) {
                 return InteractionResult.FAIL;
             } else {
-                BlockState blockState = this.getPlacementState(blockPlaceContext);
-                if (blockState == null) {
+                BlockState iblockdata = this.getPlacementState(blockactioncontext1);
+                // CraftBukkit start - special case for handling block placement with water lilies and snow buckets
+                org.bukkit.block.BlockState blockstate = null;
+                if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) {
+                    blockstate = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos());
+                }
+                final org.bukkit.block.BlockState oldBlockstate = blockstate != null ? blockstate : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); // Paper - Reset placed block on exception
+                // CraftBukkit end
+
+                if (iblockdata == null) {
                     return InteractionResult.FAIL;
-                } else if (!this.placeBlock(blockPlaceContext, blockState)) {
+                } else if (!this.placeBlock(blockactioncontext1, iblockdata)) {
                     return InteractionResult.FAIL;
                 } else {
-                    BlockPos blockPos = blockPlaceContext.getClickedPos();
-                    Level level = blockPlaceContext.getLevel();
-                    Player player = blockPlaceContext.getPlayer();
-                    ItemStack itemStack = blockPlaceContext.getItemInHand();
-                    BlockState blockState2 = level.getBlockState(blockPos);
-                    if (blockState2.is(blockState.getBlock())) {
-                        blockState2 = this.updateBlockStateFromTag(blockPos, level, itemStack, blockState2);
-                        this.updateCustomBlockEntityTag(blockPos, level, player, itemStack, blockState2);
-                        updateBlockEntityComponents(level, blockPos, itemStack);
-                        blockState2.getBlock().setPlacedBy(level, blockPos, blockState2, player, itemStack);
-                        if (player instanceof ServerPlayer) {
-                            CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer)player, blockPos, itemStack);
+                    BlockPos blockposition = blockactioncontext1.getClickedPos();
+                    Level world = blockactioncontext1.getLevel();
+                    Player entityhuman = blockactioncontext1.getPlayer();
+                    ItemStack itemstack = blockactioncontext1.getItemInHand();
+                    BlockState iblockdata1 = world.getBlockState(blockposition);
+
+                    if (iblockdata1.is(iblockdata.getBlock())) {
+                        iblockdata1 = this.updateBlockStateFromTag(blockposition, world, itemstack, iblockdata1);
+                        // Paper start - Reset placed block on exception
+                        try {
+                        this.updateCustomBlockEntityTag(blockposition, world, entityhuman, itemstack, iblockdata1);
+                        BlockItem.updateBlockEntityComponents(world, blockposition, itemstack);
+                        } catch (Exception e) {
+                            oldBlockstate.update(true, false);
+                            if (entityhuman instanceof ServerPlayer player) {
+                                org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), e);
+                                player.getBukkitEntity().kickPlayer("Packet processing error");
+                                return InteractionResult.FAIL;
+                            }
+                            throw e; // Rethrow exception if not placed by a player
+                        }
+                        // Paper end - Reset placed block on exception
+                        iblockdata1.getBlock().setPlacedBy(world, blockposition, iblockdata1, entityhuman, itemstack);
+                        // CraftBukkit start
+                        if (blockstate != null) {
+                            org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent((ServerLevel) world, entityhuman, blockactioncontext1.getHand(), blockstate, blockposition.getX(), blockposition.getY(), blockposition.getZ());
+                            if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
+                                blockstate.update(true, false);
+
+                                if (true) { // Paper - if the event is called here, the inventory should be updated
+                                    ((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541
+                                }
+                                return InteractionResult.FAIL;
+                            }
+                        }
+                        // CraftBukkit end
+                        if (entityhuman instanceof ServerPlayer) {
+                            CriteriaTriggers.PLACED_BLOCK.trigger((ServerPlayer) entityhuman, blockposition, itemstack);
                         }
                     }
 
-                    SoundType soundType = blockState2.getSoundType();
-                    level.playSound(
-                        player,
-                        blockPos,
-                        this.getPlaceSound(blockState2),
-                        SoundSource.BLOCKS,
-                        (soundType.getVolume() + 1.0F) / 2.0F,
-                        soundType.getPitch() * 0.8F
-                    );
-                    level.gameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Context.of(player, blockState2));
-                    itemStack.consume(1, player);
-                    return InteractionResult.sidedSuccess(level.isClientSide);
+                    SoundType soundeffecttype = iblockdata1.getSoundType();
+
+                    if (entityhuman == null) world.playSound(entityhuman, blockposition, this.getPlaceSound(iblockdata1), net.minecraft.sounds.SoundSource.BLOCKS, (soundeffecttype.getVolume() + 1.0F) / 2.0F, soundeffecttype.getPitch() * 0.8F); // Paper - Fix block place logic; reintroduce this for the dispenser (i.e the shulker)
+                    world.gameEvent((Holder) GameEvent.BLOCK_PLACE, blockposition, GameEvent.Context.of(entityhuman, iblockdata1));
+                    itemstack.consume(1, entityhuman);
+                    return InteractionResult.sidedSuccess(world.isClientSide);
                 }
             }
         }
@@ -109,42 +147,55 @@
     }
 
     private static void updateBlockEntityComponents(Level world, BlockPos pos, ItemStack stack) {
-        BlockEntity blockEntity = world.getBlockEntity(pos);
-        if (blockEntity != null) {
-            blockEntity.applyComponentsFromItemStack(stack);
-            blockEntity.setChanged();
+        BlockEntity tileentity = world.getBlockEntity(pos);
+
+        if (tileentity != null) {
+            tileentity.applyComponentsFromItemStack(stack);
+            tileentity.setChanged();
         }
+
     }
 
     protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) {
-        return updateCustomBlockEntityTag(world, player, pos, stack);
+        return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack);
     }
 
     @Nullable
     protected BlockState getPlacementState(BlockPlaceContext context) {
-        BlockState blockState = this.getBlock().getStateForPlacement(context);
-        return blockState != null && this.canPlace(context, blockState) ? blockState : null;
+        BlockState iblockdata = this.getBlock().getStateForPlacement(context);
+
+        return iblockdata != null && this.canPlace(context, iblockdata) ? iblockdata : null;
     }
 
     private BlockState updateBlockStateFromTag(BlockPos pos, Level world, ItemStack stack, BlockState state) {
-        BlockItemStateProperties blockItemStateProperties = stack.getOrDefault(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY);
-        if (blockItemStateProperties.isEmpty()) {
+        BlockItemStateProperties blockitemstateproperties = (BlockItemStateProperties) stack.getOrDefault(DataComponents.BLOCK_STATE, BlockItemStateProperties.EMPTY);
+
+        if (blockitemstateproperties.isEmpty()) {
             return state;
         } else {
-            BlockState blockState = blockItemStateProperties.apply(state);
-            if (blockState != state) {
-                world.setBlock(pos, blockState, 2);
+            BlockState iblockdata1 = blockitemstateproperties.apply(state);
+
+            if (iblockdata1 != state) {
+                world.setBlock(pos, iblockdata1, 2);
             }
 
-            return blockState;
+            return iblockdata1;
         }
     }
 
     protected boolean canPlace(BlockPlaceContext context, BlockState state) {
-        Player player = context.getPlayer();
-        CollisionContext collisionContext = player == null ? CollisionContext.empty() : CollisionContext.of(player);
-        return (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos()))
-            && context.getLevel().isUnobstructed(state, context.getClickedPos(), collisionContext);
+        Player entityhuman = context.getPlayer();
+        CollisionContext voxelshapecollision = entityhuman == null ? CollisionContext.empty() : CollisionContext.of(entityhuman);
+        // CraftBukkit start - store default return
+        Level world = context.getLevel(); // Paper - Cancel hit for vanished players
+        boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper - Cancel hit for vanished players
+        org.bukkit.entity.Player player = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null;
+
+        BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(context.getLevel(), context.getClickedPos()), player, CraftBlockData.fromData(state), defaultReturn, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand())); // Paper - Expose hand in BlockCanBuildEvent
+        context.getLevel().getCraftServer().getPluginManager().callEvent(event);
+
+        return event.isBuildable();
+        // CraftBukkit end
     }
 
     protected boolean mustSurvive() {
@@ -156,19 +207,22 @@
     }
 
     public static boolean updateCustomBlockEntityTag(Level world, @Nullable Player player, BlockPos pos, ItemStack stack) {
-        MinecraftServer minecraftServer = world.getServer();
-        if (minecraftServer == null) {
+        MinecraftServer minecraftserver = world.getServer();
+
+        if (minecraftserver == null) {
             return false;
         } else {
-            CustomData customData = stack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY);
-            if (!customData.isEmpty()) {
-                BlockEntity blockEntity = world.getBlockEntity(pos);
-                if (blockEntity != null) {
-                    if (world.isClientSide || !blockEntity.onlyOpCanSetNbt() || player != null && player.canUseGameMasterBlocks()) {
-                        return customData.loadInto(blockEntity, world.registryAccess());
+            CustomData customdata = (CustomData) stack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY);
+
+            if (!customdata.isEmpty()) {
+                BlockEntity tileentity = world.getBlockEntity(pos);
+
+                if (tileentity != null) {
+                    if (!world.isClientSide && tileentity.onlyOpCanSetNbt() && (player == null || !(player.canUseGameMasterBlocks() || (player.getAbilities().instabuild && player.getBukkitEntity().hasPermission("minecraft.nbt.place"))))) { // Spigot - add permission
+                        return false;
                     }
 
-                    return false;
+                    return customdata.loadInto(tileentity, world.registryAccess());
                 }
             }
 
@@ -202,10 +256,12 @@
 
     @Override
     public void onDestroyed(ItemEntity entity) {
-        ItemContainerContents itemContainerContents = entity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
-        if (itemContainerContents != null) {
-            ItemUtils.onContainerDestroyed(entity, itemContainerContents.nonEmptyItemsCopy());
+        ItemContainerContents itemcontainercontents = (ItemContainerContents) entity.getItem().set(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
+
+        if (itemcontainercontents != null) {
+            ItemUtils.onContainerDestroyed(entity, itemcontainercontents.nonEmptyItemsCopy());
         }
+
     }
 
     public static void setBlockEntityData(ItemStack stack, BlockEntityType<?> type, CompoundTag nbt) {
@@ -216,6 +272,7 @@
             BlockEntity.addEntityType(nbt, type);
             stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(nbt));
         }
+
     }
 
     @Override
