diff --git a/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch b/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
index 089dffdd87..fc818fdb9b 100644
--- a/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
+++ b/Spigot-Server-Patches/Fix-piston-physics-inconsistency-MC-188840.patch
@@ -12,6 +12,10 @@ that can duplicate tnt, called "world eaters".
 This patch makes the piston logic retrieve the block state from the world
 prevent this from occuring.
 
+This patch also sets the moved pos to air immediately after creating
+the moving piston TE. This prevents the block from being updated from
+other physics calls by the piston.
+
 Tested against the following tnt duper design:
 https://www.youtube.com/watch?v=mS7xxNGhjxs
 
@@ -25,6 +29,8 @@ unaffected.
 
 This patch fixes https://bugs.mojang.com/browse/MC-188840
 
+This patch also fixes rail duping and carpet duping.
+
 diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -33,9 +39,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          consoleHasAllPermissions = getBoolean("settings.console-has-all-permissions", consoleHasAllPermissions);
      }
  
-+    public static boolean allowTntDuplication = false;
-+    private static void allowTntDuplication() {
-+        allowTntDuplication = getBoolean("settings.unsupported-settings.allow-tnt-duplication", allowTntDuplication);
++    public static boolean allowPistonDuplication;
++    private static void allowPistonDuplication() {
++        allowPistonDuplication = getBoolean("settings.unsupported-settings.allow-piston-duplication", config.getBoolean("settings.unsupported-settings.allow-tnt-duplication", false));
++        set("settings.unsupported-settings.allow-tnt-duplication", null);
 +    }
  }
 diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java
@@ -43,16 +50,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/server/BlockPiston.java
 +++ b/src/main/java/net/minecraft/server/BlockPiston.java
 @@ -0,0 +0,0 @@ public class BlockPiston extends BlockDirectional {
+             }
  
              for (k = list.size() - 1; k >= 0; --k) {
-                 blockposition3 = (BlockPosition) list.get(k);
+-                blockposition3 = (BlockPosition) list.get(k);
 -                iblockdata1 = world.getType(blockposition3);
-+                iblockdata1 = world.getType(blockposition3); if (!com.destroystokyo.paper.PaperConfig.allowTntDuplication) map.replace(blockposition3, iblockdata1); // Paper start - fix piston physics inconsistency
++                // Paper start - fix a variety of piston desync dupes
++                boolean allowDesync = com.destroystokyo.paper.PaperConfig.allowPistonDuplication;
++                BlockPosition oldPos = blockposition3 = (BlockPosition) list.get(k);
++                iblockdata1 = allowDesync ? world.getType(oldPos) : null;
++                // Paper end - fix a variety of piston desync dupes
                  blockposition3 = blockposition3.shift(enumdirection1);
                  map.remove(blockposition3);
                  world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68);
 -                world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false));
-+                world.setTileEntity(blockposition3, BlockPistonMoving.a(com.destroystokyo.paper.PaperConfig.allowTntDuplication ? list1.get(k) : iblockdata1, enumdirection, flag, false)); // Paper - fix piston physics inconsistency
++                // Paper start - fix a variety of piston desync dupes
++                if (!allowDesync) {
++                    iblockdata1 = world.getType(oldPos);
++                    map.replace(oldPos, iblockdata1);
++                }
++                world.setTileEntity(blockposition3, BlockPistonMoving.a(allowDesync ? list1.get(k) : iblockdata1, enumdirection, flag, false));
++                if (!allowDesync) {
++                    world.setTypeAndData(oldPos, Blocks.AIR.getBlockData(), 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block
++                }
++                // Paper end - fix a variety of piston desync dupes
                  --j;
                  aiblockdata[j] = iblockdata1;
              }
@@ -65,7 +86,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
                  if (iblockdata.isAir()) {
 -                    this.world.setTypeAndData(this.position, this.a, 84);
-+                    this.world.setTypeAndData(this.position, this.a, 84 | (com.destroystokyo.paper.PaperConfig.allowTntDuplication ? 0 : 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
++                    this.world.setTypeAndData(this.position, this.a, com.destroystokyo.paper.PaperConfig.allowPistonDuplication ? 84 : (84 | 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
                      Block.a(this.a, iblockdata, this.world, this.position, 3);
                  } else {
                      if (iblockdata.b((IBlockState) BlockProperties.C) && (Boolean) iblockdata.get(BlockProperties.C)) {