--- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java
+++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java
@@ -2,8 +2,10 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.server.level.ServerLevel;
@@ -35,8 +37,16 @@
 import net.minecraft.world.phys.shapes.CollisionContext;
 import net.minecraft.world.phys.shapes.Shapes;
 import net.minecraft.world.phys.shapes.VoxelShape;
+// CraftBukkit start
+import com.google.common.collect.ImmutableList;
+import java.util.AbstractList;
+import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlock;
+import org.bukkit.event.block.BlockPistonRetractEvent;
+import org.bukkit.event.block.BlockPistonExtendEvent;
+// CraftBukkit end
 
 public class PistonBaseBlock extends DirectionalBlock {
+
     public static final BooleanProperty EXTENDED = BlockStateProperties.EXTENDED;
     public static final int TRIGGER_EXTEND = 0;
     public static final int TRIGGER_CONTRACT = 1;
@@ -52,27 +62,27 @@
 
     public PistonBaseBlock(boolean sticky, BlockBehaviour.Properties settings) {
         super(settings);
-        this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(EXTENDED, Boolean.valueOf(false)));
+        this.registerDefaultState((BlockState) ((BlockState) ((BlockState) this.stateDefinition.any()).setValue(PistonBaseBlock.FACING, Direction.NORTH)).setValue(PistonBaseBlock.EXTENDED, false));
         this.isSticky = sticky;
     }
 
     @Override
     public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
-        if (state.getValue(EXTENDED)) {
-            switch ((Direction)state.getValue(FACING)) {
+        if ((Boolean) state.getValue(PistonBaseBlock.EXTENDED)) {
+            switch ((Direction) state.getValue(PistonBaseBlock.FACING)) {
                 case DOWN:
-                    return DOWN_AABB;
+                    return PistonBaseBlock.DOWN_AABB;
                 case UP:
                 default:
-                    return UP_AABB;
+                    return PistonBaseBlock.UP_AABB;
                 case NORTH:
-                    return NORTH_AABB;
+                    return PistonBaseBlock.NORTH_AABB;
                 case SOUTH:
-                    return SOUTH_AABB;
+                    return PistonBaseBlock.SOUTH_AABB;
                 case WEST:
-                    return WEST_AABB;
+                    return PistonBaseBlock.WEST_AABB;
                 case EAST:
-                    return EAST_AABB;
+                    return PistonBaseBlock.EAST_AABB;
             }
         } else {
             return Shapes.block();
@@ -107,38 +117,61 @@
 
     @Override
     public BlockState getStateForPlacement(BlockPlaceContext ctx) {
-        return this.defaultBlockState().setValue(FACING, ctx.getNearestLookingDirection().getOpposite()).setValue(EXTENDED, Boolean.valueOf(false));
+        return (BlockState) ((BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, ctx.getNearestLookingDirection().getOpposite())).setValue(PistonBaseBlock.EXTENDED, false);
     }
 
     private void checkIfExtend(Level world, BlockPos pos, BlockState state) {
-        Direction direction = state.getValue(FACING);
-        boolean bl = this.getNeighborSignal(world, pos, direction);
-        if (bl && !state.getValue(EXTENDED)) {
-            if ((new PistonStructureResolver(world, pos, direction, true)).resolve()) {
-                world.blockEvent(pos, this, 0, direction.get3DDataValue());
+        Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
+        boolean flag = this.getNeighborSignal(world, pos, enumdirection);
+
+        if (flag && !(Boolean) state.getValue(PistonBaseBlock.EXTENDED)) {
+            if ((new PistonStructureResolver(world, pos, enumdirection, true)).resolve()) {
+                world.blockEvent(pos, this, 0, enumdirection.get3DDataValue());
             }
-        } else if (!bl && state.getValue(EXTENDED)) {
-            BlockPos blockPos = pos.relative(direction, 2);
-            BlockState blockState = world.getBlockState(blockPos);
-            int i = 1;
-            if (blockState.is(Blocks.MOVING_PISTON) && blockState.getValue(FACING) == direction) {
-                BlockEntity blockEntity = world.getBlockEntity(blockPos);
-                if (blockEntity instanceof PistonMovingBlockEntity) {
-                    PistonMovingBlockEntity pistonMovingBlockEntity = (PistonMovingBlockEntity)blockEntity;
-                    if (pistonMovingBlockEntity.isExtending() && (pistonMovingBlockEntity.getProgress(0.0F) < 0.5F || world.getGameTime() == pistonMovingBlockEntity.getLastTicked() || ((ServerLevel)world).isHandlingTick())) {
-                        i = 2;
+        } else if (!flag && (Boolean) state.getValue(PistonBaseBlock.EXTENDED)) {
+            BlockPos blockposition1 = pos.relative(enumdirection, 2);
+            BlockState iblockdata1 = world.getBlockState(blockposition1);
+            byte b0 = 1;
+
+            if (iblockdata1.is(Blocks.MOVING_PISTON) && iblockdata1.getValue(PistonBaseBlock.FACING) == enumdirection) {
+                BlockEntity tileentity = world.getBlockEntity(blockposition1);
+
+                if (tileentity instanceof PistonMovingBlockEntity) {
+                    PistonMovingBlockEntity tileentitypiston = (PistonMovingBlockEntity) tileentity;
+
+                    if (tileentitypiston.isExtending() && (tileentitypiston.getProgress(0.0F) < 0.5F || world.getGameTime() == tileentitypiston.getLastTicked() || ((ServerLevel) world).isHandlingTick())) {
+                        b0 = 2;
                     }
                 }
             }
 
-            world.blockEvent(pos, this, i, direction.get3DDataValue());
+            // CraftBukkit start
+            // if (!this.isSticky) { // Paper - Move further down
+            //     org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+            //     BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection));
+            //     world.getCraftServer().getPluginManager().callEvent(event);
+            //
+            //     if (event.isCancelled()) {
+            //         return;
+            //     }
+            // }
+            // PAIL: checkME - what happened to setTypeAndData?
+            // CraftBukkit end
+            world.blockEvent(pos, this, b0, enumdirection.get3DDataValue());
         }
 
     }
 
     private boolean getNeighborSignal(Level world, BlockPos pos, Direction pistonFace) {
-        for(Direction direction : Direction.values()) {
-            if (direction != pistonFace && world.hasSignal(pos.relative(direction), direction)) {
+        Direction[] aenumdirection = Direction.values();
+        int i = aenumdirection.length;
+
+        int j;
+
+        for (j = 0; j < i; ++j) {
+            Direction enumdirection1 = aenumdirection[j];
+
+            if (enumdirection1 != pistonFace && world.hasSignal(pos.relative(enumdirection1), enumdirection1)) {
                 return true;
             }
         }
@@ -146,10 +179,15 @@
         if (world.hasSignal(pos, Direction.DOWN)) {
             return true;
         } else {
-            BlockPos blockPos = pos.above();
+            BlockPos blockposition1 = pos.above();
+            Direction[] aenumdirection1 = Direction.values();
 
-            for(Direction direction2 : Direction.values()) {
-                if (direction2 != Direction.DOWN && world.hasSignal(blockPos.relative(direction2), direction2)) {
+            j = aenumdirection1.length;
+
+            for (int k = 0; k < j; ++k) {
+                Direction enumdirection2 = aenumdirection1[k];
+
+                if (enumdirection2 != Direction.DOWN && world.hasSignal(blockposition1.relative(enumdirection2), enumdirection2)) {
                     return true;
                 }
             }
@@ -160,66 +198,100 @@
 
     @Override
     public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
-        Direction direction = state.getValue(FACING);
+        Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
+        // Paper start - prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
+        Direction directionQueuedAs = Direction.from3DDataValue(data & 7); // Paper - copied from below
+        if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && enumdirection != directionQueuedAs) {
+            return false;
+        }
+        // Paper end - prevent retracting when we're facing the wrong way
+
         if (!world.isClientSide) {
-            boolean bl = this.getNeighborSignal(world, pos, direction);
-            if (bl && (type == 1 || type == 2)) {
-                world.setBlock(pos, state.setValue(EXTENDED, Boolean.valueOf(true)), 2);
+            boolean flag = this.getNeighborSignal(world, pos, enumdirection);
+
+            if (flag && (type == 1 || type == 2)) {
+                world.setBlock(pos, (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true), 2);
                 return false;
             }
 
-            if (!bl && type == 0) {
+            if (!flag && type == 0) {
                 return false;
             }
         }
 
         if (type == 0) {
-            if (!this.moveBlocks(world, pos, direction, true)) {
+            if (!this.moveBlocks(world, pos, enumdirection, true)) {
                 return false;
             }
 
-            world.setBlock(pos, state.setValue(EXTENDED, Boolean.valueOf(true)), 67);
-            world.playSound((Player)null, pos, SoundEvents.PISTON_EXTEND, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.25F + 0.6F);
-            world.gameEvent((Entity)null, GameEvent.PISTON_EXTEND, pos);
+            world.setBlock(pos, (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true), 67);
+            world.playSound((Player) null, pos, SoundEvents.PISTON_EXTEND, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.25F + 0.6F);
+            world.gameEvent((Entity) null, GameEvent.PISTON_EXTEND, pos);
         } else if (type == 1 || type == 2) {
-            BlockEntity blockEntity = world.getBlockEntity(pos.relative(direction));
-            if (blockEntity instanceof PistonMovingBlockEntity) {
-                ((PistonMovingBlockEntity)blockEntity).finalTick();
+            BlockEntity tileentity = world.getBlockEntity(pos.relative(enumdirection));
+
+            if (tileentity instanceof PistonMovingBlockEntity) {
+                ((PistonMovingBlockEntity) tileentity).finalTick();
             }
 
-            BlockState blockState = Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, direction).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
-            world.setBlock(pos, blockState, 20);
-            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, blockState, this.defaultBlockState().setValue(FACING, Direction.from3DDataValue(data & 7)), direction, false, true));
-            world.blockUpdated(pos, blockState.getBlock());
-            blockState.updateNeighbourShapes(world, pos, 2);
+            BlockState iblockdata1 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
+
+            // Paper start - Move empty piston retract call to fix multiple event fires
+            if (!this.isSticky) {
+                if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) {
+                    return false;
+                }
+            }
+            // Paper end
+            world.setBlock(pos, iblockdata1, 20);
+            world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
+            world.blockUpdated(pos, iblockdata1.getBlock());
+            iblockdata1.updateNeighbourShapes(world, pos, 2);
             if (this.isSticky) {
-                BlockPos blockPos = pos.offset(direction.getStepX() * 2, direction.getStepY() * 2, direction.getStepZ() * 2);
-                BlockState blockState2 = world.getBlockState(blockPos);
-                boolean bl2 = false;
-                if (blockState2.is(Blocks.MOVING_PISTON)) {
-                    BlockEntity blockEntity2 = world.getBlockEntity(blockPos);
-                    if (blockEntity2 instanceof PistonMovingBlockEntity) {
-                        PistonMovingBlockEntity pistonMovingBlockEntity = (PistonMovingBlockEntity)blockEntity2;
-                        if (pistonMovingBlockEntity.getDirection() == direction && pistonMovingBlockEntity.isExtending()) {
-                            pistonMovingBlockEntity.finalTick();
-                            bl2 = true;
+                BlockPos blockposition1 = pos.offset(enumdirection.getStepX() * 2, enumdirection.getStepY() * 2, enumdirection.getStepZ() * 2);
+                BlockState iblockdata2 = world.getBlockState(blockposition1);
+                boolean flag1 = false;
+
+                if (iblockdata2.is(Blocks.MOVING_PISTON)) {
+                    BlockEntity tileentity1 = world.getBlockEntity(blockposition1);
+
+                    if (tileentity1 instanceof PistonMovingBlockEntity) {
+                        PistonMovingBlockEntity tileentitypiston = (PistonMovingBlockEntity) tileentity1;
+
+                        if (tileentitypiston.getDirection() == enumdirection && tileentitypiston.isExtending()) {
+                            tileentitypiston.finalTick();
+                            flag1 = true;
                         }
                     }
                 }
 
-                if (!bl2) {
-                    if (type != 1 || blockState2.isAir() || !isPushable(blockState2, world, blockPos, direction.getOpposite(), false, direction) || blockState2.getPistonPushReaction() != PushReaction.NORMAL && !blockState2.is(Blocks.PISTON) && !blockState2.is(Blocks.STICKY_PISTON)) {
-                        world.removeBlock(pos.relative(direction), false);
+                if (!flag1) {
+                    if (type == 1 && !iblockdata2.isAir() && PistonBaseBlock.isPushable(iblockdata2, world, blockposition1, enumdirection.getOpposite(), false, enumdirection) && (iblockdata2.getPistonPushReaction() == PushReaction.NORMAL || iblockdata2.is(Blocks.PISTON) || iblockdata2.is(Blocks.STICKY_PISTON))) {
+                        this.moveBlocks(world, pos, enumdirection, false);
                     } else {
-                        this.moveBlocks(world, pos, direction, false);
+                        // Paper start - fire BlockPistonRetractEvent for sticky pistons retracting nothing (air)
+                        if (type == TRIGGER_CONTRACT && iblockdata2.isAir()) {
+                            if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) {
+                                return false;
+                            }
+                        }
+                        // Paper end
+                        world.removeBlock(pos.relative(enumdirection), false);
                     }
                 }
             } else {
-                world.removeBlock(pos.relative(direction), false);
+                // Paper start - fix headless pistons breaking blocks
+                BlockPos headPos = pos.relative(enumdirection);
+                if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || world.getBlockState(headPos) == Blocks.PISTON_HEAD.defaultBlockState().setValue(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
+                    world.removeBlock(headPos, false);
+                } else {
+                    ((ServerLevel)world).getChunkSource().blockChanged(headPos); // ... fix client desync
+                }
+                // Paper end - fix headless pistons breaking blocks
             }
 
-            world.playSound((Player)null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
-            world.gameEvent((Entity)null, GameEvent.PISTON_CONTRACT, pos);
+            world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
+            world.gameEvent((Entity) null, GameEvent.PISTON_CONTRACT, pos);
         }
 
         return true;
@@ -248,7 +320,7 @@
                             case PUSH_ONLY:
                                 return direction == pistonDir;
                         }
-                    } else if (state.getValue(EXTENDED)) {
+                    } else if ((Boolean) state.getValue(PistonBaseBlock.EXTENDED)) {
                         return false;
                     }
 
@@ -263,94 +335,170 @@
     }
 
     private boolean moveBlocks(Level world, BlockPos pos, Direction dir, boolean retract) {
-        BlockPos blockPos = pos.relative(dir);
-        if (!retract && world.getBlockState(blockPos).is(Blocks.PISTON_HEAD)) {
-            world.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 20);
+        BlockPos blockposition1 = pos.relative(dir);
+
+        if (!retract && world.getBlockState(blockposition1).is(Blocks.PISTON_HEAD)) {
+            world.setBlock(blockposition1, Blocks.AIR.defaultBlockState(), 20);
         }
 
-        PistonStructureResolver pistonStructureResolver = new PistonStructureResolver(world, pos, dir, retract);
-        if (!pistonStructureResolver.resolve()) {
+        PistonStructureResolver pistonextendschecker = new PistonStructureResolver(world, pos, dir, retract);
+
+        if (!pistonextendschecker.resolve()) {
             return false;
         } else {
             Map<BlockPos, BlockState> map = Maps.newHashMap();
-            List<BlockPos> list = pistonStructureResolver.getToPush();
-            List<BlockState> list2 = Lists.newArrayList();
+            List<BlockPos> list = pistonextendschecker.getToPush();
+            List<BlockState> list1 = Lists.newArrayList();
 
-            for(int i = 0; i < list.size(); ++i) {
-                BlockPos blockPos2 = list.get(i);
-                BlockState blockState = world.getBlockState(blockPos2);
-                list2.add(blockState);
-                map.put(blockPos2, blockState);
+            for (int i = 0; i < list.size(); ++i) {
+                BlockPos blockposition2 = (BlockPos) list.get(i);
+                BlockState iblockdata = world.getBlockState(blockposition2);
+
+                list1.add(iblockdata);
+                map.put(blockposition2, iblockdata);
             }
 
-            List<BlockPos> list3 = pistonStructureResolver.getToDestroy();
-            BlockState[] blockStates = new BlockState[list.size() + list3.size()];
-            Direction direction = retract ? dir : dir.getOpposite();
+            List<BlockPos> list2 = pistonextendschecker.getToDestroy();
+            BlockState[] aiblockdata = new BlockState[list.size() + list2.size()];
+            Direction enumdirection1 = retract ? dir : dir.getOpposite();
             int j = 0;
+            // CraftBukkit start
+            final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
 
-            for(int k = list3.size() - 1; k >= 0; --k) {
-                BlockPos blockPos3 = list3.get(k);
-                BlockState blockState2 = world.getBlockState(blockPos3);
-                BlockEntity blockEntity = blockState2.hasBlockEntity() ? world.getBlockEntity(blockPos3) : null;
-                dropResources(blockState2, world, blockPos3, blockEntity);
-                world.setBlock(blockPos3, Blocks.AIR.defaultBlockState(), 18);
-                world.gameEvent(GameEvent.BLOCK_DESTROY, blockPos3, GameEvent.Context.of(blockState2));
-                if (!blockState2.is(BlockTags.FIRE)) {
-                    world.addDestroyBlockEffect(blockPos3, blockState2);
+            final List<BlockPos> moved = pistonextendschecker.getToPush();
+            final List<BlockPos> broken = pistonextendschecker.getToDestroy();
+
+            List<org.bukkit.block.Block> blocks = new AbstractList<org.bukkit.block.Block>() {
+
+                @Override
+                public int size() {
+                    return moved.size() + broken.size();
                 }
 
-                blockStates[j++] = blockState2;
+                @Override
+                public org.bukkit.block.Block get(int index) {
+                    if (index >= size() || index < 0) {
+                        throw new ArrayIndexOutOfBoundsException(index);
+                    }
+                    BlockPos pos = (BlockPos) (index < moved.size() ? moved.get(index) : broken.get(index - moved.size()));
+                    return bblock.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+                }
+            };
+            org.bukkit.event.block.BlockPistonEvent event;
+            if (retract) {
+                event = new BlockPistonExtendEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1));
+            } else {
+                event = new BlockPistonRetractEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1));
             }
+            world.getCraftServer().getPluginManager().callEvent(event);
 
-            for(int l = list.size() - 1; l >= 0; --l) {
-                BlockPos blockPos4 = list.get(l);
-                BlockState blockState3 = world.getBlockState(blockPos4);
-                blockPos4 = blockPos4.relative(direction);
-                map.remove(blockPos4);
-                BlockState blockState4 = Blocks.MOVING_PISTON.defaultBlockState().setValue(FACING, dir);
-                world.setBlock(blockPos4, blockState4, 68);
-                world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockPos4, blockState4, list2.get(l), dir, retract, false));
-                blockStates[j++] = blockState3;
+            if (event.isCancelled()) {
+                for (BlockPos b : broken) {
+                    world.sendBlockUpdated(b, Blocks.AIR.defaultBlockState(), world.getBlockState(b), 3);
+                }
+                for (BlockPos b : moved) {
+                    world.sendBlockUpdated(b, Blocks.AIR.defaultBlockState(), world.getBlockState(b), 3);
+                    b = b.relative(enumdirection1);
+                    world.sendBlockUpdated(b, Blocks.AIR.defaultBlockState(), world.getBlockState(b), 3);
+                }
+                return false;
+            }
+            // CraftBukkit end
+
+            BlockPos blockposition3;
+            int k;
+            BlockState iblockdata1;
+
+            for (k = list2.size() - 1; k >= 0; --k) {
+                blockposition3 = (BlockPos) list2.get(k);
+                iblockdata1 = world.getBlockState(blockposition3);
+                BlockEntity tileentity = iblockdata1.hasBlockEntity() ? world.getBlockEntity(blockposition3) : null;
+
+                dropResources(iblockdata1, world, blockposition3, tileentity, pos); // Paper
+                world.setBlock(blockposition3, Blocks.AIR.defaultBlockState(), 18);
+                world.gameEvent(GameEvent.BLOCK_DESTROY, blockposition3, GameEvent.Context.of(iblockdata1));
+                if (!iblockdata1.is(BlockTags.FIRE)) {
+                    world.addDestroyBlockEffect(blockposition3, iblockdata1);
+                }
+
+                aiblockdata[j++] = iblockdata1;
+            }
+
+            for (k = list.size() - 1; k >= 0; --k) {
+                // Paper start - fix a variety of piston desync dupes
+                boolean allowDesync = io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication;
+                BlockPos oldPos = blockposition3 = (BlockPos) list.get(k);
+                iblockdata1 = allowDesync ? world.getBlockState(oldPos) : null;
+                // Paper end - fix a variety of piston desync dupes
+                blockposition3 = blockposition3.relative(enumdirection1);
+                map.remove(blockposition3);
+                BlockState iblockdata2 = (BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, dir);
+
+                world.setBlock(blockposition3, iblockdata2, 68);
+                // Paper start - fix a variety of piston desync dupes
+                if (!allowDesync) {
+                    iblockdata1 = world.getBlockState(oldPos);
+                    map.replace(oldPos, iblockdata1);
+                }
+                world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition3, iblockdata2, allowDesync ? list1.get(k) : iblockdata1, dir, retract, false));
+                if (!allowDesync) {
+                    world.setBlock(oldPos, Blocks.AIR.defaultBlockState(), 2 | 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block
+                }
+                // Paper end - fix a variety of piston desync dupes
+                aiblockdata[j++] = iblockdata1;
             }
 
             if (retract) {
-                PistonType pistonType = this.isSticky ? PistonType.STICKY : PistonType.DEFAULT;
-                BlockState blockState5 = Blocks.PISTON_HEAD.defaultBlockState().setValue(PistonHeadBlock.FACING, dir).setValue(PistonHeadBlock.TYPE, pistonType);
-                BlockState blockState6 = Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, dir).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
-                map.remove(blockPos);
-                world.setBlock(blockPos, blockState6, 68);
-                world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockPos, blockState6, blockState5, dir, true, true));
+                PistonType blockpropertypistontype = this.isSticky ? PistonType.STICKY : PistonType.DEFAULT;
+                BlockState iblockdata3 = (BlockState) ((BlockState) Blocks.PISTON_HEAD.defaultBlockState().setValue(PistonHeadBlock.FACING, dir)).setValue(PistonHeadBlock.TYPE, blockpropertypistontype);
+
+                iblockdata1 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, dir)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
+                map.remove(blockposition1);
+                world.setBlock(blockposition1, iblockdata1, 68);
+                world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition1, iblockdata1, iblockdata3, dir, true, true));
             }
 
-            BlockState blockState7 = Blocks.AIR.defaultBlockState();
+            BlockState iblockdata4 = Blocks.AIR.defaultBlockState();
+            Iterator iterator = map.keySet().iterator();
 
-            for(BlockPos blockPos5 : map.keySet()) {
-                world.setBlock(blockPos5, blockState7, 82);
+            while (iterator.hasNext()) {
+                BlockPos blockposition4 = (BlockPos) iterator.next();
+
+                world.setBlock(blockposition4, iblockdata4, 82);
             }
 
-            for(Map.Entry<BlockPos, BlockState> entry : map.entrySet()) {
-                BlockPos blockPos6 = entry.getKey();
-                BlockState blockState8 = entry.getValue();
-                blockState8.updateIndirectNeighbourShapes(world, blockPos6, 2);
-                blockState7.updateNeighbourShapes(world, blockPos6, 2);
-                blockState7.updateIndirectNeighbourShapes(world, blockPos6, 2);
+            iterator = map.entrySet().iterator();
+
+            BlockPos blockposition5;
+
+            while (iterator.hasNext()) {
+                Entry<BlockPos, BlockState> entry = (Entry) iterator.next();
+
+                blockposition5 = (BlockPos) entry.getKey();
+                BlockState iblockdata5 = (BlockState) entry.getValue();
+
+                iblockdata5.updateIndirectNeighbourShapes(world, blockposition5, 2);
+                iblockdata4.updateNeighbourShapes(world, blockposition5, 2);
+                iblockdata4.updateIndirectNeighbourShapes(world, blockposition5, 2);
             }
 
             j = 0;
 
-            for(int m = list3.size() - 1; m >= 0; --m) {
-                BlockState blockState9 = blockStates[j++];
-                BlockPos blockPos7 = list3.get(m);
-                blockState9.updateIndirectNeighbourShapes(world, blockPos7, 2);
-                world.updateNeighborsAt(blockPos7, blockState9.getBlock());
+            int l;
+
+            for (l = list2.size() - 1; l >= 0; --l) {
+                iblockdata1 = aiblockdata[j++];
+                blockposition5 = (BlockPos) list2.get(l);
+                iblockdata1.updateIndirectNeighbourShapes(world, blockposition5, 2);
+                world.updateNeighborsAt(blockposition5, iblockdata1.getBlock());
             }
 
-            for(int n = list.size() - 1; n >= 0; --n) {
-                world.updateNeighborsAt(list.get(n), blockStates[j++].getBlock());
+            for (l = list.size() - 1; l >= 0; --l) {
+                world.updateNeighborsAt((BlockPos) list.get(l), aiblockdata[j++].getBlock());
             }
 
             if (retract) {
-                world.updateNeighborsAt(blockPos, Blocks.PISTON_HEAD);
+                world.updateNeighborsAt(blockposition1, Blocks.PISTON_HEAD);
             }
 
             return true;
@@ -359,22 +507,22 @@
 
     @Override
     public BlockState rotate(BlockState state, Rotation rotation) {
-        return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
+        return (BlockState) state.setValue(PistonBaseBlock.FACING, rotation.rotate((Direction) state.getValue(PistonBaseBlock.FACING)));
     }
 
     @Override
     public BlockState mirror(BlockState state, Mirror mirror) {
-        return state.rotate(mirror.getRotation(state.getValue(FACING)));
+        return state.rotate(mirror.getRotation((Direction) state.getValue(PistonBaseBlock.FACING)));
     }
 
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
-        builder.add(FACING, EXTENDED);
+        builder.add(PistonBaseBlock.FACING, PistonBaseBlock.EXTENDED);
     }
 
     @Override
     public boolean useShapeForLightOcclusion(BlockState state) {
-        return state.getValue(EXTENDED);
+        return (Boolean) state.getValue(PistonBaseBlock.EXTENDED);
     }
 
     @Override
