From 50072bc2ece4ade2161d1ba49114b4c79b214d38 Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Thu, 13 Aug 2026 18:56:31 +0200 Subject: [PATCH 1/2] fix(tmp): try make clipboard patterns safe for Filter --- .../clipboard/ConcurrentReadClipboard.java | 151 ++++++++++++++++++ .../function/pattern/ClipboardPattern.java | 3 +- 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java new file mode 100644 index 0000000000..62b61bc1d7 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java @@ -0,0 +1,151 @@ +package com.fastasyncworldedit.core.extent.clipboard; + +import com.fastasyncworldedit.core.nbt.FaweCompoundTag; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.jetbrains.annotations.ApiStatus; + +import javax.annotation.Nonnull; +import java.util.Iterator; +import java.util.List; + +@ApiStatus.Experimental // Temporary wrapper +public class ConcurrentReadClipboard implements Clipboard { + + private final Clipboard parent; + + public static Clipboard tryWrap(Clipboard clipboard) { + if (clipboard instanceof ConcurrentReadClipboard + || clipboard instanceof WorldCopyClipboard + || clipboard instanceof EmptyClipboard + || clipboard instanceof DiskOptimizedClipboard // FastSchematicReaderV2/V3 no min offset + || (clipboard instanceof BlockArrayClipboard blockArrayClipboard + && blockArrayClipboard.getParent() instanceof DiskOptimizedClipboard)) { + return clipboard; + } + return new ConcurrentReadClipboard(clipboard); + } + + private ConcurrentReadClipboard(Clipboard parent) { + this.parent = parent; + } + + @Override + public synchronized BlockState getBlock(BlockVector3 position) { + return parent.getBlock(position); + } + + @Override + public synchronized BlockState getBlock(int x, int y, int z) { + return parent.getBlock(x, y, z); + } + + @Override + public synchronized BaseBlock getFullBlock(BlockVector3 position) { + return parent.getFullBlock(position); + } + + @Override + public synchronized BaseBlock getFullBlock(int x, int y, int z) { + return parent.getFullBlock(x, y, z); + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + return parent.getBiomeType(x, y, z); + } + + @Override + public BiomeType getBiome(BlockVector3 position) { + return parent.getBiome(position); + } + + @Override + public > boolean setBlock(int x, int y, int z, B block) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + public boolean tile(int x, int y, int z, FaweCompoundTag tile) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + public boolean setBiome(BlockVector3 position, BiomeType biome) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + public BlockVector3 getMinimumPoint() { + return parent.getMinimumPoint(); + } + + @Override + public BlockVector3 getMaximumPoint() { + return parent.getMaximumPoint(); + } + + @Override + public List getEntities(Region region) { + return parent.getEntities(region); + } + + @Override + public List getEntities() { + return parent.getEntities(); + } + + @Override + public Region getRegion() { + return parent.getRegion(); + } + + @Override + public BlockVector3 getDimensions() { + return parent.getDimensions(); + } + + @Override + public BlockVector3 getOrigin() { + return parent.getOrigin(); + } + + @Override + public void setOrigin(BlockVector3 origin) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + public boolean hasBiomes() { + return parent.hasBiomes(); + } + + @Override + public void removeEntity(Entity entity) { + throw new UnsupportedOperationException("Wrapper not intended for writing"); + } + + @Override + @Nonnull + public Iterator iterator() { + return parent.iterator(); + } + + @Override + public void close() { + parent.close(); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java index b0fac9c400..a614e41a04 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.function.pattern; +import com.fastasyncworldedit.core.extent.clipboard.ConcurrentReadClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; @@ -43,7 +44,7 @@ public ClipboardPattern(Clipboard clipboard) { * @param offset the offset */ public ClipboardPattern(Clipboard clipboard, BlockVector3 offset) { - super(clipboard, clipboard.getMinimumPoint(), offset); + super(ConcurrentReadClipboard.tryWrap(clipboard), clipboard.getMinimumPoint(), offset); } } From 900f8b121c6cb3ab6656bd537fa14d9a4bbad998 Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Sat, 15 Aug 2026 14:38:24 +0200 Subject: [PATCH 2/2] feat: add way to check if a clip supports concurrent reads -> currently "wrongly" marked true in ConcurrentReadClipboard although it only wraps block reads --- .../extent/clipboard/ConcurrentReadClipboard.java | 12 ++++++------ .../extent/clipboard/DiskOptimizedClipboard.java | 5 +++++ .../core/extent/clipboard/EmptyClipboard.java | 5 +++++ .../core/extent/clipboard/WorldCopyClipboard.java | 5 +++++ .../extent/clipboard/BlockArrayClipboard.java | 5 +++++ .../sk89q/worldedit/extent/clipboard/Clipboard.java | 9 +++++++++ 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java index 62b61bc1d7..e9054ba5ff 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ConcurrentReadClipboard.java @@ -22,12 +22,7 @@ public class ConcurrentReadClipboard implements Clipboard { private final Clipboard parent; public static Clipboard tryWrap(Clipboard clipboard) { - if (clipboard instanceof ConcurrentReadClipboard - || clipboard instanceof WorldCopyClipboard - || clipboard instanceof EmptyClipboard - || clipboard instanceof DiskOptimizedClipboard // FastSchematicReaderV2/V3 no min offset - || (clipboard instanceof BlockArrayClipboard blockArrayClipboard - && blockArrayClipboard.getParent() instanceof DiskOptimizedClipboard)) { + if (clipboard.supportsParallelAccess()) { return clipboard; } return new ConcurrentReadClipboard(clipboard); @@ -143,6 +138,11 @@ public Iterator iterator() { return parent.iterator(); } + @Override + public boolean supportsParallelAccess() { + return true; + } + @Override public void close() { parent.close(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index dc2ba45059..e074fa8dc9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -303,6 +303,11 @@ public URI getURI() { return file.toURI(); } + @Override + public boolean supportsParallelAccess() { + return true; + } + public File getFile() { return file; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java index 983ea79b0d..774b2432f6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/EmptyClipboard.java @@ -46,6 +46,11 @@ public void setOrigin(@Nonnull BlockVector3 origin) { public void removeEntity(@Nonnull Entity entity) { } + @Override + public boolean supportsParallelAccess() { + return true; + } + @Nonnull public BlockVector3 getMinimumPoint() { return BlockVector3.ZERO; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java index b4e30abacd..8669eda5cb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java @@ -102,6 +102,11 @@ public boolean hasBiomes() { return hasBiomes; } + @Override + public boolean supportsParallelAccess() { + return true; + } + @Override public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pasteEntities, boolean pasteBiomes) { boolean close = false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index 9ff666f799..a6f0264042 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -385,6 +385,11 @@ public Clipboard getParent() { return parent; } + @Override + public boolean supportsParallelAccess() { + return parent.supportsParallelAccess(); + } + @Override public void close() { this.parent.close(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index be702ae4d4..537ce96dd7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -216,6 +216,15 @@ default URI getURI() { return null; } + /** + * Check if this clipboard supports concurrent reading. + * + * @since TODO + */ + default boolean supportsParallelAccess() { + return false; + } + @Override default T apply(Region region, T filter, boolean full) { if (region.equals(getRegion())) {