--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -1,7 +1,10 @@
 package net.minecraft.world.item;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
 import javax.annotation.Nullable;
 import net.minecraft.advancements.CriteriaTriggers;
 import net.minecraft.core.BlockPos;
@@ -9,9 +12,9 @@
 import net.minecraft.nbt.ListTag;
 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;
@@ -29,8 +32,13 @@
 import net.minecraft.world.level.block.state.properties.Property;
 import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.phys.shapes.CollisionContext;
+import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlock;
+import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
+import org.bukkit.event.block.BlockCanBuildEvent;
+// CraftBukkit end
 
 public class BlockItem extends Item {
+
     public static final String BLOCK_ENTITY_TAG = "BlockEntityTag";
     public static final String BLOCK_STATE_TAG = "BlockStateTag";
     /** @deprecated */
@@ -44,12 +52,14 @@
 
     @Override
     public InteractionResult useOn(UseOnContext context) {
-        InteractionResult interactionResult = this.place(new BlockPlaceContext(context));
-        if (!interactionResult.consumesAction() && this.isEdible()) {
-            InteractionResult interactionResult2 = this.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() && this.isEdible()) {
+            InteractionResult enuminteractionresult1 = this.use(context.getLevel(), context.getPlayer(), context.getHand()).getResult();
+
+            return enuminteractionresult1 == InteractionResult.CONSUME ? InteractionResult.CONSUME_PARTIAL : enuminteractionresult1;
         } else {
-            return interactionResult;
+            return enuminteractionresult;
         }
     }
 
@@ -59,38 +69,75 @@
         } 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.v1_20_R2.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos());
+                }
+                final org.bukkit.block.BlockState oldBlockstate = blockstate != null ? blockstate : org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); // Paper
+                // 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);
-                        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 block on exception
+                        try {
+                        this.updateCustomBlockEntityTag(blockposition, world, entityhuman, itemstack, iblockdata1);
+                        } 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
+                        iblockdata1.getBlock().setPlacedBy(world, blockposition, iblockdata1, entityhuman, itemstack);
+                        // CraftBukkit start
+                        if (blockstate != null) {
+                            org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.v1_20_R2.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).connection.send(new net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket(world, blockposition.below())); // Paper - update block below
+                                    ((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));
-                    if (player == null || !player.getAbilities().instabuild) {
-                        itemStack.shrink(1);
+                    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 - reintroduce this for the dispenser (i.e the shulker)
+                    world.gameEvent(GameEvent.BLOCK_PLACE, blockposition, GameEvent.Context.of(entityhuman, iblockdata1));
+                    if ((entityhuman == null || !entityhuman.getAbilities().instabuild) && itemstack != ItemStack.EMPTY) { // CraftBukkit
+                        itemstack.shrink(1);
                     }
 
-                    return InteractionResult.sidedSuccess(level.isClientSide);
+                    return InteractionResult.sidedSuccess(world.isClientSide);
                 }
             }
         }
@@ -106,48 +153,73 @@
     }
 
     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) {
-        BlockState blockState = state;
-        CompoundTag compoundTag = stack.getTag();
-        if (compoundTag != null) {
-            CompoundTag compoundTag2 = compoundTag.getCompound("BlockStateTag");
-            StateDefinition<Block, BlockState> stateDefinition = state.getBlock().getStateDefinition();
+        BlockState iblockdata1 = state;
+        CompoundTag nbttagcompound = stack.getTag();
 
-            for(String string : compoundTag2.getAllKeys()) {
-                Property<?> property = stateDefinition.getProperty(string);
-                if (property != null) {
-                    String string2 = compoundTag2.get(string).getAsString();
-                    blockState = updateState(blockState, property, string2);
-                }
-            }
+        if (nbttagcompound != null) {
+            CompoundTag nbttagcompound1 = nbttagcompound.getCompound("BlockStateTag");
+            // CraftBukkit start
+            iblockdata1 = BlockItem.getBlockState(iblockdata1, nbttagcompound1);
         }
 
-        if (blockState != state) {
-            world.setBlock(pos, blockState, 2);
+        if (iblockdata1 != state) {
+            world.setBlock(pos, iblockdata1, 2);
         }
 
-        return blockState;
+        return iblockdata1;
+    }
+
+    public static BlockState getBlockState(BlockState iblockdata, CompoundTag nbttagcompound1) {
+        BlockState iblockdata1 = iblockdata;
+        {
+            // CraftBukkit end
+            StateDefinition<Block, BlockState> blockstatelist = iblockdata.getBlock().getStateDefinition();
+            Iterator iterator = nbttagcompound1.getAllKeys().iterator();
+
+            while (iterator.hasNext()) {
+                String s = (String) iterator.next();
+                Property<?> iblockstate = blockstatelist.getProperty(s);
+
+                if (iblockstate != null) {
+                    String s1 = nbttagcompound1.get(s).getAsString();
+
+                    iblockdata1 = BlockItem.updateState(iblockdata1, iblockstate, s1);
+                }
+            }
+        }
+        return iblockdata1;
     }
 
     private static <T extends Comparable<T>> BlockState updateState(BlockState state, Property<T> property, String name) {
-        return property.getValue(name).map((value) -> {
-            return state.setValue(property, value);
+        return (BlockState) property.getValue(name).map((comparable) -> {
+            return (BlockState) state.setValue(property, comparable);
         }).orElse(state);
     }
 
     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
+        boolean defaultReturn = (!this.mustSurvive() || state.canSurvive(context.getLevel(), context.getClickedPos())) && world.checkEntityCollision(state, entityhuman, voxelshapecollision, context.getClickedPos(), true); // Paper
+        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.v1_20_R2.CraftEquipmentSlot.getHand(context.getHand())); // Paper - expose hand
+        context.getLevel().getCraftServer().getPluginManager().callEvent(event);
+
+        return event.isBuildable();
+        // CraftBukkit end
     }
 
     protected boolean mustSurvive() {
@@ -159,24 +231,28 @@
     }
 
     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 {
-            CompoundTag compoundTag = getBlockEntityData(stack);
-            if (compoundTag != null) {
-                BlockEntity blockEntity = world.getBlockEntity(pos);
-                if (blockEntity != null) {
-                    if (!world.isClientSide && blockEntity.onlyOpCanSetNbt() && (player == null || !player.canUseGameMasterBlocks())) {
+            CompoundTag nbttagcompound = BlockItem.getBlockEntityData(stack);
+
+            if (nbttagcompound != null) {
+                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;
                     }
 
-                    CompoundTag compoundTag2 = blockEntity.saveWithoutMetadata();
-                    CompoundTag compoundTag3 = compoundTag2.copy();
-                    compoundTag2.merge(compoundTag);
-                    if (!compoundTag2.equals(compoundTag3)) {
-                        blockEntity.load(compoundTag2);
-                        blockEntity.setChanged();
+                    CompoundTag nbttagcompound1 = tileentity.saveWithoutMetadata();
+                    CompoundTag nbttagcompound2 = nbttagcompound1.copy();
+
+                    nbttagcompound1.merge(nbttagcompound);
+                    if (!nbttagcompound1.equals(nbttagcompound2)) {
+                        tileentity.load(nbttagcompound1);
+                        tileentity.setChanged();
                         return true;
                     }
                 }
@@ -213,11 +289,15 @@
     @Override
     public void onDestroyed(ItemEntity entity) {
         if (this.block instanceof ShulkerBoxBlock) {
-            ItemStack itemStack = entity.getItem();
-            CompoundTag compoundTag = getBlockEntityData(itemStack);
-            if (compoundTag != null && compoundTag.contains("Items", 9)) {
-                ListTag listTag = compoundTag.getList("Items", 10);
-                ItemUtils.onContainerDestroyed(entity, listTag.stream().map(CompoundTag.class::cast).map(ItemStack::of));
+            ItemStack itemstack = entity.getItem();
+            CompoundTag nbttagcompound = BlockItem.getBlockEntityData(itemstack);
+
+            if (nbttagcompound != null && nbttagcompound.contains("Items", 9)) {
+                ListTag nbttaglist = nbttagcompound.getList("Items", 10);
+                Stream<net.minecraft.nbt.Tag> stream = nbttaglist.stream(); // CraftBukkit - decompile error
+
+                Objects.requireNonNull(CompoundTag.class);
+                ItemUtils.onContainerDestroyed(entity, stream.map(CompoundTag.class::cast).map(ItemStack::of));
             }
         }
 
