--- a/net/minecraft/world/level/block/TrapDoorBlock.java
+++ b/net/minecraft/world/level/block/TrapDoorBlock.java
@@ -6,8 +6,10 @@
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
+import net.minecraft.core.Holder;
 import net.minecraft.sounds.SoundSource;
 import net.minecraft.world.InteractionResult;
+import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.context.BlockPlaceContext;
@@ -24,65 +26,61 @@
 import net.minecraft.world.level.block.state.properties.EnumProperty;
 import net.minecraft.world.level.block.state.properties.Half;
 import net.minecraft.world.level.gameevent.GameEvent;
+import net.minecraft.world.level.material.Fluid;
 import net.minecraft.world.level.material.FluidState;
 import net.minecraft.world.level.material.Fluids;
 import net.minecraft.world.level.pathfinder.PathComputationType;
 import net.minecraft.world.phys.BlockHitResult;
 import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
 
 public class TrapDoorBlock extends HorizontalDirectionalBlock implements SimpleWaterloggedBlock {
-    public static final MapCodec<TrapDoorBlock> CODEC = RecordCodecBuilder.mapCodec(
-        instance -> instance.group(BlockSetType.CODEC.fieldOf("block_set_type").forGetter(block -> block.type), propertiesCodec())
-                .apply(instance, TrapDoorBlock::new)
-    );
+
+    public static final MapCodec<TrapDoorBlock> CODEC = RecordCodecBuilder.mapCodec((instance) -> {
+        return instance.group(BlockSetType.CODEC.fieldOf("block_set_type").forGetter((blocktrapdoor) -> {
+            return blocktrapdoor.type;
+        }), propertiesCodec()).apply(instance, TrapDoorBlock::new);
+    });
     public static final BooleanProperty OPEN = BlockStateProperties.OPEN;
     public static final EnumProperty<Half> HALF = BlockStateProperties.HALF;
     public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
     public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
     protected static final int AABB_THICKNESS = 3;
-    protected static final VoxelShape EAST_OPEN_AABB = Block.box(0.0, 0.0, 0.0, 3.0, 16.0, 16.0);
-    protected static final VoxelShape WEST_OPEN_AABB = Block.box(13.0, 0.0, 0.0, 16.0, 16.0, 16.0);
-    protected static final VoxelShape SOUTH_OPEN_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 3.0);
-    protected static final VoxelShape NORTH_OPEN_AABB = Block.box(0.0, 0.0, 13.0, 16.0, 16.0, 16.0);
-    protected static final VoxelShape BOTTOM_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 3.0, 16.0);
-    protected static final VoxelShape TOP_AABB = Block.box(0.0, 13.0, 0.0, 16.0, 16.0, 16.0);
+    protected static final VoxelShape EAST_OPEN_AABB = Block.box(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D);
+    protected static final VoxelShape WEST_OPEN_AABB = Block.box(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
+    protected static final VoxelShape SOUTH_OPEN_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D);
+    protected static final VoxelShape NORTH_OPEN_AABB = Block.box(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D);
+    protected static final VoxelShape BOTTOM_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D);
+    protected static final VoxelShape TOP_AABB = Block.box(0.0D, 13.0D, 0.0D, 16.0D, 16.0D, 16.0D);
     private final BlockSetType type;
 
     @Override
     public MapCodec<? extends TrapDoorBlock> codec() {
-        return CODEC;
+        return TrapDoorBlock.CODEC;
     }
 
     protected TrapDoorBlock(BlockSetType type, BlockBehaviour.Properties settings) {
         super(settings.sound(type.soundType()));
         this.type = type;
-        this.registerDefaultState(
-            this.stateDefinition
-                .any()
-                .setValue(FACING, Direction.NORTH)
-                .setValue(OPEN, Boolean.valueOf(false))
-                .setValue(HALF, Half.BOTTOM)
-                .setValue(POWERED, Boolean.valueOf(false))
-                .setValue(WATERLOGGED, Boolean.valueOf(false))
-        );
+        this.registerDefaultState((BlockState) ((BlockState) ((BlockState) ((BlockState) ((BlockState) ((BlockState) this.stateDefinition.any()).setValue(TrapDoorBlock.FACING, Direction.NORTH)).setValue(TrapDoorBlock.OPEN, false)).setValue(TrapDoorBlock.HALF, Half.BOTTOM)).setValue(TrapDoorBlock.POWERED, false)).setValue(TrapDoorBlock.WATERLOGGED, false));
     }
 
     @Override
     protected VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        if (!state.getValue(OPEN)) {
-            return state.getValue(HALF) == Half.TOP ? TOP_AABB : BOTTOM_AABB;
+        if (!(Boolean) state.getValue(TrapDoorBlock.OPEN)) {
+            return state.getValue(TrapDoorBlock.HALF) == Half.TOP ? TrapDoorBlock.TOP_AABB : TrapDoorBlock.BOTTOM_AABB;
         } else {
-            switch ((Direction)state.getValue(FACING)) {
+            switch ((Direction) state.getValue(TrapDoorBlock.FACING)) {
                 case NORTH:
                 default:
-                    return NORTH_OPEN_AABB;
+                    return TrapDoorBlock.NORTH_OPEN_AABB;
                 case SOUTH:
-                    return SOUTH_OPEN_AABB;
+                    return TrapDoorBlock.SOUTH_OPEN_AABB;
                 case WEST:
-                    return WEST_OPEN_AABB;
+                    return TrapDoorBlock.WEST_OPEN_AABB;
                 case EAST:
-                    return EAST_OPEN_AABB;
+                    return TrapDoorBlock.EAST_OPEN_AABB;
             }
         }
     }
@@ -91,11 +89,11 @@
     protected boolean isPathfindable(BlockState state, PathComputationType type) {
         switch (type) {
             case LAND:
-                return state.getValue(OPEN);
+                return (Boolean) state.getValue(TrapDoorBlock.OPEN);
             case WATER:
-                return state.getValue(WATERLOGGED);
+                return (Boolean) state.getValue(TrapDoorBlock.WATERLOGGED);
             case AIR:
-                return state.getValue(OPEN);
+                return (Boolean) state.getValue(TrapDoorBlock.OPEN);
             default:
                 return false;
         }
@@ -113,82 +111,114 @@
 
     @Override
     protected void onExplosionHit(BlockState state, Level world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
-        if (explosion.canTriggerBlocks() && this.type.canOpenByWindCharge() && !state.getValue(POWERED)) {
-            this.toggle(state, world, pos, null);
+        if (explosion.canTriggerBlocks() && this.type.canOpenByWindCharge() && !(Boolean) state.getValue(TrapDoorBlock.POWERED)) {
+            this.toggle(state, world, pos, (Player) null);
         }
 
         super.onExplosionHit(state, world, pos, explosion, stackMerger);
     }
 
     private void toggle(BlockState state, Level world, BlockPos pos, @Nullable Player player) {
-        BlockState blockState = state.cycle(OPEN);
-        world.setBlock(pos, blockState, 2);
-        if (blockState.getValue(WATERLOGGED)) {
-            world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
+        BlockState iblockdata1 = (BlockState) state.cycle(TrapDoorBlock.OPEN);
+
+        world.setBlock(pos, iblockdata1, 2);
+        if ((Boolean) iblockdata1.getValue(TrapDoorBlock.WATERLOGGED)) {
+            world.scheduleTick(pos, (Fluid) Fluids.WATER, Fluids.WATER.getTickDelay(world));
         }
 
-        this.playSound(player, world, pos, blockState.getValue(OPEN));
+        this.playSound(player, world, pos, (Boolean) iblockdata1.getValue(TrapDoorBlock.OPEN));
     }
 
     protected void playSound(@Nullable Player player, Level world, BlockPos pos, boolean open) {
-        world.playSound(
-            player, pos, open ? this.type.trapdoorOpen() : this.type.trapdoorClose(), SoundSource.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F
-        );
-        world.gameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
+        world.playSound(player, pos, open ? this.type.trapdoorOpen() : this.type.trapdoorClose(), SoundSource.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F);
+        world.gameEvent((Entity) player, (Holder) (open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE), pos);
     }
 
     @Override
     protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
         if (!world.isClientSide) {
-            boolean bl = world.hasNeighborSignal(pos);
-            if (bl != state.getValue(POWERED)) {
-                if (state.getValue(OPEN) != bl) {
-                    state = state.setValue(OPEN, Boolean.valueOf(bl));
-                    this.playSound(null, world, pos, bl);
+            boolean flag1 = world.hasNeighborSignal(pos);
+
+            if (flag1 != (Boolean) state.getValue(TrapDoorBlock.POWERED)) {
+                // CraftBukkit start
+                org.bukkit.World bworld = world.getWorld();
+                org.bukkit.block.Block bblock = bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+
+                int power = bblock.getBlockPower();
+                int oldPower = (Boolean) state.getValue(TrapDoorBlock.OPEN) ? 15 : 0;
+
+                if (oldPower == 0 ^ power == 0 || sourceBlock.defaultBlockState().isSignalSource()) {
+                    BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bblock, oldPower, power);
+                    world.getCraftServer().getPluginManager().callEvent(eventRedstone);
+                    flag1 = eventRedstone.getNewCurrent() > 0;
+                }
+                // CraftBukkit end
+                // Paper start - break redstone on trapdoors early
+                boolean open = (Boolean) state.getValue(TrapDoorBlock.OPEN) != flag1;
+                // note: this must run before any state for this block/its neighborus are written to the world
+                // we allow the redstone event to fire so that plugins can block
+                if (flag1 && open) { // if we are now powered and it caused the trap door to open
+                    // in this case, first check for the redstone on top first
+                    BlockPos abovePos = pos.above();
+                    BlockState above = world.getBlockState(abovePos);
+                    if (above.getBlock() instanceof RedStoneWireBlock) {
+                        world.setBlock(abovePos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_NEIGHBORS);
+                        Block.popResource(world, abovePos, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.REDSTONE));
+                        // now check that this didn't change our state
+                        if (world.getBlockState(pos) != state) {
+                            // our state was changed, so we cannot propagate this update
+                            return;
+                        }
+                    }
+                }
+                if (open) {
+                // Paper end - break redstone on trapdoors early
+                    state = (BlockState) state.setValue(TrapDoorBlock.OPEN, flag1);
+                    this.playSound((Player) null, world, pos, flag1);
                 }
 
-                world.setBlock(pos, state.setValue(POWERED, Boolean.valueOf(bl)), 2);
-                if (state.getValue(WATERLOGGED)) {
-                    world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
+                world.setBlock(pos, (BlockState) state.setValue(TrapDoorBlock.POWERED, flag1), 2);
+                if ((Boolean) state.getValue(TrapDoorBlock.WATERLOGGED)) {
+                    world.scheduleTick(pos, (Fluid) Fluids.WATER, Fluids.WATER.getTickDelay(world));
                 }
             }
+
         }
     }
 
     @Override
     public BlockState getStateForPlacement(BlockPlaceContext ctx) {
-        BlockState blockState = this.defaultBlockState();
-        FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos());
-        Direction direction = ctx.getClickedFace();
-        if (!ctx.replacingClickedOnBlock() && direction.getAxis().isHorizontal()) {
-            blockState = blockState.setValue(FACING, direction)
-                .setValue(HALF, ctx.getClickLocation().y - (double)ctx.getClickedPos().getY() > 0.5 ? Half.TOP : Half.BOTTOM);
+        BlockState iblockdata = this.defaultBlockState();
+        FluidState fluid = ctx.getLevel().getFluidState(ctx.getClickedPos());
+        Direction enumdirection = ctx.getClickedFace();
+
+        if (!ctx.replacingClickedOnBlock() && enumdirection.getAxis().isHorizontal()) {
+            iblockdata = (BlockState) ((BlockState) iblockdata.setValue(TrapDoorBlock.FACING, enumdirection)).setValue(TrapDoorBlock.HALF, ctx.getClickLocation().y - (double) ctx.getClickedPos().getY() > 0.5D ? Half.TOP : Half.BOTTOM);
         } else {
-            blockState = blockState.setValue(FACING, ctx.getHorizontalDirection().getOpposite())
-                .setValue(HALF, direction == Direction.UP ? Half.BOTTOM : Half.TOP);
+            iblockdata = (BlockState) ((BlockState) iblockdata.setValue(TrapDoorBlock.FACING, ctx.getHorizontalDirection().getOpposite())).setValue(TrapDoorBlock.HALF, enumdirection == Direction.UP ? Half.BOTTOM : Half.TOP);
         }
 
         if (ctx.getLevel().hasNeighborSignal(ctx.getClickedPos())) {
-            blockState = blockState.setValue(OPEN, Boolean.valueOf(true)).setValue(POWERED, Boolean.valueOf(true));
+            iblockdata = (BlockState) ((BlockState) iblockdata.setValue(TrapDoorBlock.OPEN, true)).setValue(TrapDoorBlock.POWERED, true);
         }
 
-        return blockState.setValue(WATERLOGGED, Boolean.valueOf(fluidState.getType() == Fluids.WATER));
+        return (BlockState) iblockdata.setValue(TrapDoorBlock.WATERLOGGED, fluid.getType() == Fluids.WATER);
     }
 
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
-        builder.add(FACING, OPEN, HALF, POWERED, WATERLOGGED);
+        builder.add(TrapDoorBlock.FACING, TrapDoorBlock.OPEN, TrapDoorBlock.HALF, TrapDoorBlock.POWERED, TrapDoorBlock.WATERLOGGED);
     }
 
     @Override
     protected FluidState getFluidState(BlockState state) {
-        return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
+        return (Boolean) state.getValue(TrapDoorBlock.WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
     }
 
     @Override
     protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
-        if (state.getValue(WATERLOGGED)) {
-            world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
+        if ((Boolean) state.getValue(TrapDoorBlock.WATERLOGGED)) {
+            world.scheduleTick(pos, (Fluid) Fluids.WATER, Fluids.WATER.getTickDelay(world));
         }
 
         return super.updateShape(state, direction, neighborState, world, pos, neighborPos);
