From d7cc65d2f238467e9fc4539e8dbeb9ef448f3598 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 3 Jul 2023 16:37:42 +0100 Subject: [PATCH 01/38] refactor: some changes that may help #2289 (#2307) --- .../com/sk89q/worldedit/LocalSession.java | 4 +- .../worldedit/session/SessionManager.java | 4 +- .../session/storage/JsonFileSessionStore.java | 70 +++++++++++-------- .../sk89q/worldedit/world/item/ItemType.java | 12 ++-- 4 files changed, 50 insertions(+), 40 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 1ef2af6d6..dfa29a2e3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -105,8 +105,8 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class LocalSession implements TextureHolder { - private static final transient int CUI_VERSION_UNINITIALIZED = -1; - public static transient int MAX_HISTORY_SIZE = 15; + public static int MAX_HISTORY_SIZE = 15; + private static final int CUI_VERSION_UNINITIALIZED = -1; // Non-session related fields private transient LocalConfiguration config; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java index d93a6b298..646b18bc0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionManager.java @@ -45,8 +45,8 @@ import com.sk89q.worldedit.world.item.ItemTypes; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -354,7 +354,7 @@ public class SessionManager { @Subscribe public void onConfigurationLoad(ConfigurationLoadEvent event) { LocalConfiguration config = event.getConfiguration(); - File dir = new File(config.getWorkingDirectoryPath().toFile(), "sessions"); + Path dir = config.getWorkingDirectoryPath().resolve("sessions"); store = new JsonFileSessionStore(dir); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java index b383bda21..eb4b1ee1a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/storage/JsonFileSessionStore.java @@ -36,6 +36,10 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.UUID; import static com.google.common.base.Preconditions.checkNotNull; @@ -49,20 +53,31 @@ public class JsonFileSessionStore implements SessionStore { private static final Logger LOGGER = LogManagerCompat.getLogger(); private final Gson gson; - private final File dir; + private final Path dir; + + /** + * Create a new session store. + * + * @param dir the directory + * @deprecated Use {@link #JsonFileSessionStore(Path)} + */ + @Deprecated + public JsonFileSessionStore(File dir) { + this(dir.toPath()); + } /** * Create a new session store. * * @param dir the directory */ - public JsonFileSessionStore(File dir) { + public JsonFileSessionStore(Path dir) { checkNotNull(dir); - if (!dir.isDirectory()) { - if (!dir.mkdirs()) { - LOGGER.warn("Failed to create directory '" + dir.getPath() + "' for sessions"); - } + try { + Files.createDirectories(dir); + } catch (IOException e) { + LOGGER.warn("Failed to create directory '" + dir + "' for sessions", e); } this.dir = dir; @@ -77,21 +92,19 @@ public class JsonFileSessionStore implements SessionStore { * @param id the ID * @return the file */ - private File getPath(UUID id) { + private Path getPath(UUID id) { checkNotNull(id); - return new File(dir, id + ".json"); + return dir.resolve(id + ".json"); } @Override public LocalSession load(UUID id) throws IOException { - File file = getPath(id); - try (Closer closer = Closer.create()) { - FileReader fr = closer.register(new FileReader(file)); - BufferedReader br = closer.register(new BufferedReader(fr)); - LocalSession session = gson.fromJson(br, LocalSession.class); + Path file = getPath(id); + try (var reader = Files.newBufferedReader(file)) { + LocalSession session = gson.fromJson(reader, LocalSession.class); if (session == null) { LOGGER.warn("Loaded a null session from {}, creating new session", file); - if (!file.delete()) { + if (!Files.deleteIfExists(file)) { LOGGER.warn("Failed to delete corrupted session {}", file); } session = new LocalSession(); @@ -99,7 +112,7 @@ public class JsonFileSessionStore implements SessionStore { return session; } catch (JsonParseException e) { throw new IOException(e); - } catch (FileNotFoundException e) { + } catch (NoSuchFileException e) { return new LocalSession(); } } @@ -107,29 +120,26 @@ public class JsonFileSessionStore implements SessionStore { @Override public void save(UUID id, LocalSession session) throws IOException { checkNotNull(session); - File finalFile = getPath(id); - File tempFile = new File(finalFile.getParentFile(), finalFile.getName() + ".tmp"); + Path finalFile = getPath(id); + Path tempFile = finalFile.getParent().resolve(finalFile.getFileName() + ".tmp"); - try (Closer closer = Closer.create()) { - FileWriter fr = closer.register(new FileWriter(tempFile)); - BufferedWriter bw = closer.register(new BufferedWriter(fr)); - gson.toJson(session, bw); + try (var writer = Files.newBufferedWriter(tempFile)) { + gson.toJson(session, writer); } catch (JsonIOException e) { throw new IOException(e); } - if (finalFile.exists()) { - if (!finalFile.delete()) { - LOGGER.warn("Failed to delete " + finalFile.getPath() + " so the .tmp file can replace it"); - } - } - - if (tempFile.length() == 0) { + if (Files.size(tempFile) == 0) { throw new IllegalStateException("Gson wrote zero bytes"); } - if (!tempFile.renameTo(finalFile)) { - LOGGER.warn("Failed to rename temporary session file to " + finalFile.getPath()); + try { + Files.move( + tempFile, finalFile, + StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING + ); + } catch (IOException e) { + LOGGER.warn("Failed to rename temporary session file to " + finalFile.toAbsolutePath(), e); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java index ecba92f8b..5a1e7653f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java @@ -43,7 +43,7 @@ public class ItemType implements RegistryItem, Keyed { private final String id; @SuppressWarnings("deprecation") - private final LazyReference name = LazyReference.from(() -> { + private transient final LazyReference name = LazyReference.from(() -> { String name = GuavaUtil.firstNonNull( WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS) .getRegistries().getItemRegistry().getName(this), @@ -51,18 +51,18 @@ public class ItemType implements RegistryItem, Keyed { ); return name.isEmpty() ? getId() : name; }); - private final LazyReference richName = LazyReference.from(() -> + private transient final LazyReference richName = LazyReference.from(() -> WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS) .getRegistries().getItemRegistry().getRichName(this) ); - private final LazyReference itemMaterial = LazyReference.from(() -> + private transient final LazyReference itemMaterial = LazyReference.from(() -> WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS) .getRegistries().getItemRegistry().getMaterial(this) ); //FAWE start - private BlockType blockType; - private boolean initBlockType; - private BaseItem defaultState; + private transient BlockType blockType; + private transient boolean initBlockType; + private transient BaseItem defaultState; //FAWE end public ItemType(String id) { From 52a51753dd2c5e2be4094afda60a8f49a1544c2b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:50:09 +0200 Subject: [PATCH 02/38] Update dependency com.palmergames.bukkit.towny:towny to v0.99.2.7 (#2329) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4588bc54c..afda60933 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.99.2.5" +towny = "0.99.2.7" # Third party bstats = "3.0.2" From 34ead35e03fbc1894139f12acd79c0ce37084abe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:50:20 +0200 Subject: [PATCH 03/38] Update dependency net.minecrell.plugin-yml.bukkit to v0.6.0 (#2327) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index afda60933..cf04ae8f8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ piston = "0.5.7" mockito = "5.4.0" # Gradle plugins -pluginyml = "0.5.3" +pluginyml = "0.6.0" [libraries] # Minecraft expectations From ccdb002adef3041505fd5526a89bb840775a0ad3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:50:49 +0200 Subject: [PATCH 04/38] Update dependency gradle to v8.2 (#2326) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 16170 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 delta 14892 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d3f..62f495dfe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb4..fcb6fca14 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. From 67fd31ce4c3c7427714142f1d8d7d35e57a32f8c Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Wed, 5 Jul 2023 16:06:15 +0200 Subject: [PATCH 05/38] Update bom --- buildSrc/src/main/kotlin/AdapterConfig.kt | 2 +- buildSrc/src/main/kotlin/CommonJavaConfig.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/AdapterConfig.kt b/buildSrc/src/main/kotlin/AdapterConfig.kt index 9fb5595da..21cab4c73 100644 --- a/buildSrc/src/main/kotlin/AdapterConfig.kt +++ b/buildSrc/src/main/kotlin/AdapterConfig.kt @@ -15,7 +15,7 @@ fun Project.applyPaperweightAdapterConfiguration() { dependencies { "implementation"(project(":worldedit-bukkit")) - "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9")) + "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.31")) } tasks.named("assemble") { diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt index 8542fe252..759e45740 100644 --- a/buildSrc/src/main/kotlin/CommonJavaConfig.kt +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -45,7 +45,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean "testImplementation"("org.mockito:mockito-core:5.1.1") "testImplementation"("org.mockito:mockito-junit-jupiter:5.1.1") "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.9.2") - "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9")) + "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.31")) } // Java 8 turns on doclint which we fail From a669be20413bbfef91370bfba445a3ff9ddf2452 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 5 Jul 2023 22:03:13 +0100 Subject: [PATCH 06/38] fix #2319 --- .../fawe/v1_17_R1_2/PaperweightGetBlocks.java | 6 ++++- .../fawe/v1_18_R2/PaperweightGetBlocks.java | 6 ++++- .../fawe/v1_19_R3/PaperweightGetBlocks.java | 6 ++++- .../fawe/v1_20_R1/PaperweightGetBlocks.java | 6 ++++- .../bukkit/adapter/NMSAdapter.java | 27 ++++++++++--------- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java index 85be84901..aab9e5aa7 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java @@ -445,7 +445,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << layer; - char[] setArr = set.load(layerNo); + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this time. + char[] tmp = set.load(layerNo); + char[] setArr = new char[tmp.length]; + System.arraycopy(tmp, 0, setArr, 0, tmp.length); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java index 4e435e7ee..91a5abede 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java @@ -491,7 +491,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] setArr = set.load(layerNo); + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this time. + char[] tmp = set.load(layerNo); + char[] setArr = new char[tmp.length]; + System.arraycopy(tmp, 0, setArr, 0, tmp.length); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index ba1bfe574..b13a273ec 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -490,7 +490,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] setArr = set.load(layerNo); + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this time. + char[] tmp = set.load(layerNo); + char[] setArr = new char[tmp.length]; + System.arraycopy(tmp, 0, setArr, 0, tmp.length); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java index 0dd292207..8662dc2a6 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java @@ -468,7 +468,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] setArr = set.load(layerNo); + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this time. + char[] tmp = set.load(layerNo); + char[] setArr = new char[tmp.length]; + System.arraycopy(tmp, 0, setArr, 0, tmp.length); // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // submitted to keep loaded internal chunks to queue target size. diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java index b41da1679..c146153fc 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/NMSAdapter.java @@ -30,8 +30,7 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl { ordinal = BlockTypesCache.ReservedIDs.AIR; nonAir--; } - case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> - nonAir--; + case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> nonAir--; } int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { @@ -74,8 +73,6 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl { CachedBukkitAdapter adapter, short[] nonEmptyBlockCount ) { - // Write to new array to avoid editing SET array - char[] copy = new char[set.length]; short nonAir = 4096; int num_palette = 0; char[] getArr = null; @@ -86,19 +83,23 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl { if (getArr == null) { getArr = get.apply(layer); } - switch (ordinal = getArr[i]) { + // write to set array as this should be a copied array, and will be important when the changes are written + // to the GET chunk cached by FAWE. Future dords, actually read this comment please. + set[i] = switch (ordinal = getArr[i]) { case BlockTypesCache.ReservedIDs.__RESERVED__ -> { nonAir--; - ordinal = BlockTypesCache.ReservedIDs.AIR; + yield (ordinal = BlockTypesCache.ReservedIDs.AIR); } - case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> - nonAir--; - } + case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, + BlockTypesCache.ReservedIDs.VOID_AIR -> { + nonAir--; + yield ordinal; + } + default -> ordinal; + }; } - case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> - nonAir--; + case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> nonAir--; } - copy[i] = ordinal; int palette = blockToPalette[ordinal]; if (palette == Integer.MAX_VALUE) { blockToPalette[ordinal] = num_palette; @@ -116,7 +117,7 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl { System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length); } for (int i = 0; i < 4096; i++) { - char ordinal = copy[i]; + char ordinal = set[i]; if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { LOGGER.error("Empty (__RESERVED__) ordinal given where not expected, default to air."); ordinal = BlockTypesCache.ReservedIDs.AIR; From 888683d83b8d35ef27b75ad41181d2edc5dcdbd7 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 7 Jul 2023 17:23:37 +0100 Subject: [PATCH 07/38] fix: ignore clipboard origin when pasting to match P2 behaviour (#2336) --- .../bukkit/regions/plotsquared/FaweDelegateRegionManager.java | 1 + .../bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java | 1 + 2 files changed, 2 insertions(+) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java index e20c73809..66a9ef369 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java @@ -183,6 +183,7 @@ public class FaweDelegateRegionManager { .findByFile(schematicFile) .getReader(new FileInputStream(schematicFile)) .read(); + clip.setOrigin(clip.getRegion().getMinimumPoint()); clip.paste(scheditsession, to, true, true, true); } catch (IOException e) { e.printStackTrace(); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java index 83de13f66..6cf09da28 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java @@ -152,6 +152,7 @@ public class FaweDelegateSchematicHandler { final BlockVector3 to = BlockVector3 .at(region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset); final Clipboard clipboard = schematic.getClipboard(); + clipboard.setOrigin(clipboard.getRegion().getMinimumPoint()); clipboard.paste(editSession, to, true, false, true); if (whenDone != null) { whenDone.value = true; From f38c81aa2ad70634e13586968f5a7ee5e3f27386 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 7 Jul 2023 17:27:10 +0100 Subject: [PATCH 08/38] fix: correctly invalidate mask after a plot is merged (#2333) --- .../bukkit/regions/plotsquared/PlotSquaredFeature.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java index 46ba12f68..e28602292 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java @@ -211,8 +211,9 @@ public class PlotSquaredFeature extends FaweMaskManager { @Override public boolean isValid(Player player, MaskType type, boolean notify) { - if ((!connectedPlots.refersTo(plot.getConnectedPlots()) && !singlePlot) || (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone( - plot))) { + if ((!connectedPlots.refersTo(plot.getConnectedPlots()) && (!singlePlot || plot + .getConnectedPlots() + .size() > 1)) || (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot))) { return false; } return isAllowed(player, plot, type, notify); From 6fbdef5252f980ce900504a9892122945a8cd9f5 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 7 Jul 2023 17:36:02 +0100 Subject: [PATCH 09/38] feat: allow bracketless '%' pattern definition (#2322) --- .../extension/factory/parser/pattern/RichPatternParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RichPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RichPatternParser.java index 3ebd84fa6..bf2027c9b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RichPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RichPatternParser.java @@ -117,7 +117,7 @@ public class RichPatternParser extends FaweParser { if (addBrackets) { value += "["; } - value += StringMan.join(entry.getValue(), " "); + value += StringMan.join(entry.getValue(), "]["); if (addBrackets) { value += "]"; } From d78092b4ca810a3c9ecacb4e7d1e0a507e3758c9 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 7 Jul 2023 17:36:12 +0100 Subject: [PATCH 10/38] fix: ensure chunk is loaded when getting entities (#2323) --- .../bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java | 1 + 2 files changed, 2 insertions(+) diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index b13a273ec..c715e5fc2 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -332,6 +332,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { + ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptySet(); diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java index 8662dc2a6..08d2f1069 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java @@ -310,6 +310,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc @Override public Set getEntities() { + ensureLoaded(serverLevel, chunkX, chunkZ); List entities = PaperweightPlatformAdapter.getEntities(getChunk()); if (entities.isEmpty()) { return Collections.emptySet(); From 90587e56fc139d106390df3cdcdb25d5c746780e Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 7 Jul 2023 18:38:10 +0200 Subject: [PATCH 11/38] Deprecate support for 1.16.5 (#2338) --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 - build.gradle.kts | 2 +- .../java/com/fastasyncworldedit/bukkit/FaweBukkit.java | 8 ++++++++ .../fastasyncworldedit/bukkit/util/MinecraftVersion.java | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 83d9f52d8..8a134b81e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -32,7 +32,6 @@ body: - '1.19.4' - '1.18.2' - '1.17.1' - - '1.16.5' validations: required: true diff --git a/build.gradle.kts b/build.gradle.kts index 781f1fba2..de0d714cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,7 +83,7 @@ allprojects { } applyCommonConfiguration() -val supportedVersions = listOf("1.16.5", "1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.1") +val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.1") tasks { supportedVersions.forEach { diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java index 46bf125c5..2e25c8c56 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java @@ -108,6 +108,14 @@ public class FaweBukkit implements IFawe, Listener { if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) { LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?"); } + + if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) { + LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021)."); + LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future."); + LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." + + "Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " + + "support."); + } } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java index 08ee52de6..f719d430d 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; public class MinecraftVersion implements Comparable { public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16); + public static final MinecraftVersion ONE_DOT_SIXTEEN_EOL = new MinecraftVersion(1, 16, 5); public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17); public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18); private static MinecraftVersion current = null; From 68eb4e214ada9a7b6ccb3999d4db53bf8bc9d417 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 8 Jul 2023 19:41:20 +0200 Subject: [PATCH 12/38] refactor: use HttpClient for update check (#2331) --- .../core/util/UpdateNotification.java | 80 ++++++++++++------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/UpdateNotification.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/UpdateNotification.java index 6425cfd14..13effcc46 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/UpdateNotification.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/UpdateNotification.java @@ -14,7 +14,13 @@ import org.w3c.dom.Document; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import java.net.URL; +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.temporal.ChronoUnit; public class UpdateNotification { @@ -28,38 +34,54 @@ public class UpdateNotification { */ public static void doUpdateCheck() { if (Settings.settings().ENABLED_COMPONENTS.UPDATE_NOTIFICATIONS) { - try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse(new URL("https://ci.athion.net/job/FastAsyncWorldEdit/api/xml/").openStream()); - faweVersion = doc.getElementsByTagName("lastSuccessfulBuild").item(0).getFirstChild().getTextContent(); - FaweVersion faweVersion = Fawe.instance().getVersion(); - if (faweVersion.build == 0) { - LOGGER.warn("You are using a snapshot or a custom version of FAWE. This is not an official build distributed " + - "via https://www.spigotmc.org/resources/13932/"); - return; - } - if (faweVersion.build < Integer.parseInt(UpdateNotification.faweVersion)) { - hasUpdate = true; - int versionDifference = Integer.parseInt(UpdateNotification.faweVersion) - faweVersion.build; - LOGGER.warn( - """ - An update for FastAsyncWorldEdit is available. You are {} build(s) out of date. - You are running build {}, the latest version is build {}. - Update at https://www.spigotmc.org/resources/13932/""", - versionDifference, - faweVersion.build, - UpdateNotification.faweVersion - ); - } - } catch (Exception e) { - LOGGER.error("Unable to check for updates. Skipping."); - } + final HttpRequest request = HttpRequest + .newBuilder() + .uri(URI.create("https://ci.athion.net/job/FastAsyncWorldEdit/api/xml/")) + .timeout(Duration.of(10L, ChronoUnit.SECONDS)) + .build(); + HttpClient.newHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .whenComplete((response, thrown) -> { + if (thrown != null) { + LOGGER.error("Update check failed: {} ", thrown.getMessage()); + } + processResponseBody(response.body()); + }); } } + private static void processResponseBody(InputStream body) { + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(body); + faweVersion = doc.getElementsByTagName("lastSuccessfulBuild").item(0).getFirstChild().getTextContent(); + FaweVersion faweVersion = Fawe.instance().getVersion(); + if (faweVersion.build == 0) { + LOGGER.warn("You are using a snapshot or a custom version of FAWE. This is not an official build distributed " + + "via https://www.spigotmc.org/resources/13932/"); + return; + } + if (faweVersion.build < Integer.parseInt(UpdateNotification.faweVersion)) { + hasUpdate = true; + int versionDifference = Integer.parseInt(UpdateNotification.faweVersion) - faweVersion.build; + LOGGER.warn( + """ + An update for FastAsyncWorldEdit is available. You are {} build(s) out of date. + You are running build {}, the latest version is build {}. + Update at https://www.spigotmc.org/resources/13932/""", + versionDifference, + faweVersion.build, + UpdateNotification.faweVersion + ); + } + } catch (Exception ignored) { + LOGGER.error("Unable to check for updates. Skipping."); + } + } + /** * Trigger an update notification based on captions. Useful to notify server administrators ingame. * From 685248d8de979ef85bd7b0cbbae0421bcf1e4628 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 9 Jul 2023 15:16:30 +0100 Subject: [PATCH 13/38] chore: deprecate some methods that ought to be removed (#2340) --- .../core/configuration/Settings.java | 38 ++----------------- .../core/util/TaskManager.java | 16 +++++++- 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index bb5f16eb3..174d59f37 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -468,24 +468,6 @@ public class Settings extends Config { }) public int BUFFER_SIZE = 531441; - - @Comment({ - "The maximum time in milliseconds to wait for a chunk to load for an edit.", - " (50ms = 1 server tick, 0 = Fastest).", - " The default value of 100 should be safe for most cases.", - "", - "Actions which require loaded chunks (e.g. copy) which do not load in time", - " will use the last chunk as filler, which may appear as bands of duplicated blocks.", - "Actions usually wait about 25-50ms for the chunk to load, more if the server is lagging.", - "A value of 100ms does not force it to wait 100ms if the chunk loads in 10ms.", - "", - "This value is a timeout in case a chunk is never going to load (for whatever odd reason).", - "If the action times out, the operation continues by using the previous chunk as filler,", - " and displaying an error message. In this case, either copy a smaller section,", - " or increase chunk-wait-ms.", - "A value of 0 is faster simply because it doesn't bother loading the chunks or waiting.", - }) - public int CHUNK_WAIT_MS = 1000; @Comment("Delete history on disk after a number of days") public int DELETE_AFTER_DAYS = 7; @Comment("Delete history in memory on logout (does not effect disk)") @@ -493,6 +475,7 @@ public class Settings extends Config { @Comment({ "If history should be enabled by default for plugins using WorldEdit:", " - It is faster to have disabled", + " - It is faster to have disabled", " - Use of the FAWE API will not be effected" }) public boolean ENABLE_FOR_CONSOLE = true; @@ -515,10 +498,12 @@ public class Settings extends Config { @Create public static PROGRESS PROGRESS; + @Comment({ "This should equal the number of processors you have", }) public int PARALLEL_THREADS = Math.max(1, Runtime.getRuntime().availableProcessors()); + @Comment({ "When doing edits that effect more than this many chunks:", " - FAWE will start placing before all calculations are finished", @@ -530,14 +515,6 @@ public class Settings extends Config { }) public int TARGET_SIZE = 8 * Runtime.getRuntime().availableProcessors(); - @Comment({ - "Force FAWE to start placing chunks regardless of whether an edit is finished processing", - " - A larger value will use slightly less CPU time", - " - A smaller value will reduce memory usage", - " - A value too small may break some operations (deform?)" - }) - //TODO Find out where this was used and why the usage was removed - public int MAX_WAIT_MS = 1000; @Comment({ "Increase or decrease queue intensity (ms) [-50,50]:", @@ -566,13 +543,6 @@ public class Settings extends Config { }) public boolean POOL = true; - @Comment({ - "Discard edits which have been idle for a certain amount of time (ms)", - " - E.g. A plugin creates an EditSession but never does anything with it", - " - This only applies to plugins improperly using WorldEdit's legacy API" - }) - public int DISCARD_AFTER_MS = 60000; - @Comment({ "When using fastmode do not bother to tick existing/placed blocks/fluids", "Only works in versions up to 1.17.1" @@ -639,7 +609,7 @@ public class Settings extends Config { public boolean REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL = true; @Comment({ - "Other experimental features" + "Increased debug logging for brush actions and processor setup" }) public boolean OTHER = false; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java index f3abf0ac3..65025bcd0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java @@ -88,7 +88,10 @@ public abstract class TaskManager { /** * Run a bunch of tasks in parallel using the shared thread pool. + * + * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ + @Deprecated(forRemoval = true, since = "TODO") public void parallel(Collection runables) { for (Runnable run : runables) { pool.submit(run); @@ -101,8 +104,9 @@ public abstract class TaskManager { * * @param runnables the tasks to run * @param numThreads number of threads (null = config.yml parallel threads) + * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ - @Deprecated + @Deprecated(forRemoval = true, since = "TODO") public void parallel(Collection runnables, @Nullable Integer numThreads) { if (runnables == null) { return; @@ -271,13 +275,17 @@ public abstract class TaskManager { }); } + /** + * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + */ + @Deprecated(forRemoval = true, since = "TODO") public void wait(AtomicBoolean running, int timeout) { try { long start = System.currentTimeMillis(); synchronized (running) { while (running.get()) { running.wait(timeout); - if (running.get() && System.currentTimeMillis() - start > Settings.settings().QUEUE.DISCARD_AFTER_MS) { + if (running.get() && System.currentTimeMillis() - start > 60000) { new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ").printStackTrace(); LOGGER.info("For full debug information use: /fawe threads"); } @@ -288,6 +296,10 @@ public abstract class TaskManager { } } + /** + * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. + */ + @Deprecated(forRemoval = true, since = "TODO") public void notify(AtomicBoolean running) { running.set(false); synchronized (running) { From 1f29ab3b79451c028d2b3ec2032ea667a5d4090e Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 10 Jul 2023 22:04:33 +0100 Subject: [PATCH 14/38] fix: fix command brush (#2347) --- .../core/command/tool/brush/CommandBrush.java | 43 +++++++++++----- .../command/tool/brush/ScatterCommand.java | 51 +++++++------------ .../worldedit/command/BrushCommands.java | 6 ++- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java index 4072828bf..227960c1d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java @@ -22,9 +22,27 @@ import java.util.List; public class CommandBrush implements Brush { private final String command; + private final boolean print; + /** + * New instance + * + * @deprecated Use {@link CommandBrush#CommandBrush(String, boolean)} + */ + @Deprecated(forRemoval = true) public CommandBrush(String command) { - this.command = command.charAt(0) == '/' ? "/" + command : command; + this(command, false); + } + + /** + * New instance + * + * @param command command to run, or commands split by ';' + * @param print if output should be printed to the actor for the run commands + */ + public CommandBrush(String command, boolean print) { + this.command = command; + this.print = print; } @Override @@ -36,7 +54,7 @@ public class CommandBrush implements Brush { position.subtract(radius, radius, radius), position.add(radius, radius, radius) ); - String replaced = command.replace("{x}", position.getBlockX() + "") + String replaced = command.replace("{x}", Integer.toString(position.getBlockX())) .replace("{y}", Integer.toString(position.getBlockY())) .replace("{z}", Integer.toString(position.getBlockZ())) .replace("{world}", editSession.getWorld().getName()) @@ -46,21 +64,22 @@ public class CommandBrush implements Brush { if (!(actor instanceof Player player)) { throw FaweCache.PLAYER_ONLY; } - //Use max world height to allow full coverage of the world height - Location face = player.getBlockTraceFace(editSession.getWorld().getMaxY(), true); - if (face == null) { - position = position.add(0, 1, 1); - } else { - position = position.add(face.getDirection().toBlockPoint()); - } player.setSelection(selector); - AsyncPlayer wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper( + AsyncPlayer wePlayer = new LocationMaskedPlayerWrapper( player, new Location(player.getExtent(), position.toVector3()) - )); + ); + if (!print) { + wePlayer = new SilentPlayerWrapper(wePlayer); + } List cmds = StringMan.split(replaced, ';'); for (String cmd : cmds) { - CommandEvent event = new CommandEvent(wePlayer, cmd); + if (cmd.isBlank()) { + continue; + } + cmd = cmd.charAt(0) != '/' ? "/" + cmd : cmd; + cmd = cmd.length() >1 && cmd.charAt(1) == '/' ? cmd.substring(1) : cmd; + CommandEvent event = new CommandEvent(wePlayer, cmd, editSession); PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterCommand.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterCommand.java index 81be2640c..1d7841355 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterCommand.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterCommand.java @@ -3,7 +3,9 @@ package com.fastasyncworldedit.core.command.tool.brush; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.LocalBlockVectorSet; import com.fastasyncworldedit.core.util.StringMan; +import com.fastasyncworldedit.core.wrappers.AsyncPlayer; import com.fastasyncworldedit.core.wrappers.LocationMaskedPlayerWrapper; +import com.fastasyncworldedit.core.wrappers.SilentPlayerWrapper; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; @@ -13,7 +15,7 @@ import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; -import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.Location; import java.util.List; @@ -43,7 +45,7 @@ public class ScatterCommand extends ScatterBrush { position.subtract(radius, radius, radius), position.add(radius, radius, radius) ); - String replaced = command.replace("{x}", position.getBlockX() + "") + String replaced = command.replace("{x}", Integer.toString(position.getBlockX())) .replace("{y}", Integer.toString(position.getBlockY())) .replace("{z}", Integer.toString(position.getBlockZ())) .replace("{world}", editSession.getWorld().getName()) @@ -55,41 +57,22 @@ public class ScatterCommand extends ScatterBrush { } player.setSelection(selector); List cmds = StringMan.split(replaced, ';'); + AsyncPlayer wePlayer = new LocationMaskedPlayerWrapper( + player, + new Location(player.getExtent(), position.toVector3()) + ); + if (!print) { + wePlayer = new SilentPlayerWrapper(wePlayer); + } for (String cmd : cmds) { - Player p = print ? - new LocationMaskedPlayerWrapper(player, player.getLocation().setPosition(position.toVector3()), false) : - new ScatterCommandPlayerWrapper(player, position); - CommandEvent event = new CommandEvent(p, cmd, editSession); + if (cmd.isBlank()) { + continue; + } + cmd = cmd.charAt(0) != '/' ? "/" + cmd : cmd; + cmd = cmd.length() >1 && cmd.charAt(1) == '/' ? cmd.substring(1) : cmd; + CommandEvent event = new CommandEvent(wePlayer, cmd, editSession); PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event); } } - private static final class ScatterCommandPlayerWrapper extends LocationMaskedPlayerWrapper { - - ScatterCommandPlayerWrapper(Player player, BlockVector3 position) { - super(player, player.getLocation().setPosition(position.toVector3()), false); - } - - @Override - public void print(String msg) { - } - - @Override - public void print(Component component) { - } - - @Override - public void printDebug(String msg) { - } - - @Override - public void printError(String msg) { - } - - @Override - public void printRaw(String msg) { - } - - } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 358f75413..76cb60c64 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -929,11 +929,13 @@ public class BrushCommands { @Arg(desc = "Expression") Expression radius, @Arg(desc = "Command to run") - List input + List input, + @Switch(name = 'p', desc = "Show any printed output") + boolean print ) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); String cmd = StringMan.join(input, " "); - set(context, new CommandBrush(cmd), "worldedit.brush.command").setSize(radius); + set(context, new CommandBrush(cmd, print), "worldedit.brush.command").setSize(radius); } @Command( From c0e20a6fe5ec610f7ee0d9c4022ef01030792216 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 10 Jul 2023 22:16:05 +0100 Subject: [PATCH 15/38] feat: provide some information upon OverlappingFileLockException (#2341) --- .../clipboard/DiskOptimizedClipboard.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) 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 a9704e36b..3f7c746c3 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 @@ -39,6 +39,8 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -46,6 +48,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * A clipboard with disk backed storage. (lower memory + loads on crash) @@ -59,6 +62,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { private static final int HEADER_SIZE = 27; // Current header size private static final int VERSION_1_HEADER_SIZE = 22; // Header size of "version 1" private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved + private static final Map LOCK_HOLDER_CACHE = new ConcurrentHashMap<>(); private final HashMap nbtMap; private final File file; @@ -301,7 +305,23 @@ public class DiskOptimizedClipboard extends LinearClipboard { private void init() throws IOException { if (this.fileChannel == null) { this.fileChannel = braf.getChannel(); - this.fileChannel.lock(); + try { + FileLock lock = this.fileChannel.lock(); + LOCK_HOLDER_CACHE.put(file.getName(), new LockHolder(lock)); + } catch (OverlappingFileLockException e) { + LockHolder existing = LOCK_HOLDER_CACHE.get(file.getName()); + if (existing != null) { + long ms = System.currentTimeMillis() - existing.lockHeldSince; + LOGGER.error( + "Cannot lock clipboard file {} acquired by thread {}, {}ms ago", + file.getName(), + existing.thread, + ms + ); + } + // Rethrow to prevent clipboard access + throw e; + } this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, braf.length()); } } @@ -737,4 +757,18 @@ public class DiskOptimizedClipboard extends LinearClipboard { return false; } + private static class LockHolder { + + final FileLock lock; + final long lockHeldSince; + final String thread; + + LockHolder(FileLock lock) { + this.lock = lock; + lockHeldSince = System.currentTimeMillis(); + this.thread = Thread.currentThread().getName(); + } + } + + } From 0a19f643b65f1f9b7c3fb79b67c123ed8f11a2ba Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Tue, 11 Jul 2023 16:39:53 +0100 Subject: [PATCH 16/38] fix #2349 --- .../plotsquared/PlotSquaredFeature.java | 10 +++- .../core/regions/FaweMask.java | 7 ++- .../worldedit/command/UtilityCommands.java | 58 +++++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java index e28602292..a34fac51f 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java @@ -23,9 +23,11 @@ import com.sk89q.worldedit.world.World; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; +import javax.annotation.Nonnull; import java.lang.ref.WeakReference; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -192,6 +194,10 @@ public class PlotSquaredFeature extends FaweMaskManager { maskedRegion = new RegionIntersection(world, weRegions); } + if (plot == null) { + return new FaweMask(maskedRegion); + } + return new PlotSquaredMask(maskedRegion, finalPlot); } @@ -201,9 +207,9 @@ public class PlotSquaredFeature extends FaweMaskManager { private final WeakReference> connectedPlots; private final boolean singlePlot; - private PlotSquaredMask(Region region, Plot plot) { + private PlotSquaredMask(@Nonnull Region region, @Nonnull Plot plot) { super(region); - this.plot = plot; + this.plot = Objects.requireNonNull(plot); Set connected = plot.getConnectedPlots(); connectedPlots = new WeakReference<>(connected); singlePlot = connected.size() == 1; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java index 4ea8c2c06..478ac90c9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java @@ -4,12 +4,15 @@ import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.regions.Region; +import javax.annotation.Nonnull; +import java.util.Objects; + public class FaweMask implements IDelegateRegion { private final Region region; - public FaweMask(Region region) { - this.region = region; + public FaweMask(@Nonnull Region region) { + this.region = Objects.requireNonNull(region); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index f064868b4..aca13bc46 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -50,12 +50,19 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.BoundedHeightMask; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.visitor.DownwardVisitor; import com.sk89q.worldedit.function.visitor.EntityVisitor; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.VertHeight; import com.sk89q.worldedit.internal.expression.EvaluationException; @@ -63,8 +70,10 @@ import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector2; +import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; +import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.formatting.component.SubtleFormat; import com.sk89q.worldedit.util.formatting.text.Component; @@ -836,6 +845,55 @@ public class UtilityCommands { } } +// @Command( +// name = "/hollowr", +// desc = "Hollow out a space recursively with a pattern" +// ) +// @CommandPermissions("worldedit.hollowr") +// @Logging(PLACEMENT) +// public int hollowr( +// Actor actor, +// LocalSession session, +// EditSession editSession, +// @Arg(desc = "The radius to hollow out") Expression radiusExp, +// @ArgFlag(name = 'p', desc = "The blocks to fill with") Pattern pattern, +// @ArgFlag(name = 'm', desc = "The blocks remove", def = "") Mask mask +// ) throws WorldEditException { +// //FAWE start +// double radius = radiusExp.evaluate(); +// //FAWE end +// radius = Math.max(1, radius); +// we.checkMaxRadius(radius); +// if (mask == null) { +// Mask mask = new MaskIntersection( +// new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))), +// new BoundedHeightMask( +// Math.max(lowerBound, minY), +// Math.min(maxY, origin.getBlockY()) +// ), +// Masks.negate(new ExistingBlockMask(this)) +// ); +// } +// +// // Want to replace blocks +// BlockReplace replace = new BlockReplace(this, pattern); +// +// // Pick how we're going to visit blocks +// RecursiveVisitor visitor; +// //FAWE start - provide extent for preloading, min/max y +// if (recursive) { +// visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), minY, maxY, this); +// } else { +// visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), minY, maxY, this); +// } +// //FAWE end +// +// BlockVector3 pos = session.getPlacementPosition(actor); +// int affected = editSession.res(pos, pattern, radius, depth, true); +// actor.print(Caption.of("worldedit.fillr.created", TextComponent.of(affected))); +// return affected; +// } + public static List> filesToEntry(final File root, final List files, final UUID uuid) { return files.stream() .map(input -> { // Keep this functional, as transform is evaluated lazily From a680c7ce97321da0fbb3afd13cf69684a8544e52 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Thu, 13 Jul 2023 17:08:18 +0100 Subject: [PATCH 17/38] fix: fix #2352 --- .../sk89q/worldedit/extension/platform/AbstractPlayerActor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index d02819dc6..6adf7236c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -709,7 +709,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { @Override public void checkPermission(String permission) throws AuthorizationException { if (!hasPermission(permission)) { - throw new AuthorizationException(); + throw new AuthorizationException(Caption.of("fawe.error.no-perm", permission)); } } From 1745c50878435cb24aeed98ee70f8caad9692b6c Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 15 Jul 2023 09:26:00 +0200 Subject: [PATCH 18/38] Improve error behaviour of //generate --- .../src/main/java/com/sk89q/worldedit/EditSession.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index f8a08148b..780ba90e1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2991,9 +2991,10 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } catch (ExpressionTimeoutException e) { timedOut[0] = timedOut[0] + 1; return null; + } catch (RuntimeException e) { + throw e; } catch (Exception e) { - LOGGER.warn("Failed to create shape", e); - return null; + throw new RuntimeException(e); } } }; From 8da530ee8044bf37ea129cab12c0df6a337bdbb8 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 15 Jul 2023 12:27:33 +0100 Subject: [PATCH 19/38] gracefully handle error in EntityInBlockRemovingProcessor - ensures edit continues on chunk correctly --- .../EntityInBlockRemovingProcessor.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java index 0a8839409..1ffea1266 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java @@ -6,8 +6,10 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypes; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; /** @@ -17,27 +19,33 @@ import org.jetbrains.annotations.Nullable; */ public class EntityInBlockRemovingProcessor implements IBatchProcessor { + private static final Logger LOGGER = LogManagerCompat.getLogger(); + @Override public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - for (CompoundTag tag : get.getEntities()) { - // Empty tags for seemingly non-existent entities can exist? - if (tag.getList("Pos").size() == 0) { - continue; - } - BlockVector3 pos = tag.getEntityPosition().toBlockPoint(); - int x = pos.getX() & 15; - int y = pos.getY(); - int z = pos.getZ() & 15; - if (!set.hasSection(y >> 4)) { - continue; - } - if (set.getBlock(x, y, z).getBlockType() != BlockTypes.__RESERVED__ && !set - .getBlock(x, y, z) - .getBlockType() - .getMaterial() - .isAir()) { - set.removeEntity(tag.getUUID()); + try { + for (CompoundTag tag : get.getEntities()) { + // Empty tags for seemingly non-existent entities can exist? + if (tag.getList("Pos").size() == 0) { + continue; + } + BlockVector3 pos = tag.getEntityPosition().toBlockPoint(); + int x = pos.getX() & 15; + int y = pos.getY(); + int z = pos.getZ() & 15; + if (!set.hasSection(y >> 4)) { + continue; + } + if (set.getBlock(x, y, z).getBlockType() != BlockTypes.__RESERVED__ && !set + .getBlock(x, y, z) + .getBlockType() + .getMaterial() + .isAir()) { + set.removeEntity(tag.getUUID()); + } } + } catch (Exception e) { + LOGGER.warn("Could not remove entities in blocks in chunk {},{}", chunk.getX(), chunk.getZ(), e); } return set; } From f4da4b0287d431b30cf39f77cf6089683bb38d32 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 15 Jul 2023 16:40:29 +0100 Subject: [PATCH 20/38] fix: fix adding/getting masks to/from MaskExtent when it is a processor (#2351) - fixes #2350 --- .../core/util/ProcessorTraverser.java | 44 +++++++++++++++++++ .../java/com/sk89q/worldedit/EditSession.java | 32 ++++++++++---- 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ProcessorTraverser.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ProcessorTraverser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ProcessorTraverser.java new file mode 100644 index 000000000..3c3a601a1 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ProcessorTraverser.java @@ -0,0 +1,44 @@ +package com.fastasyncworldedit.core.util; + +import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder; +import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor; +import com.fastasyncworldedit.core.queue.IBatchProcessor; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayDeque; +import java.util.Queue; + +public class ProcessorTraverser { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private final T root; + + public ProcessorTraverser(@Nonnull T root) { + this.root = root; + } + + public @Nullable U find(Class clazz) { + try { + Queue processors = new ArrayDeque<>(); + IBatchProcessor processor = root; + do { + if (clazz.isAssignableFrom(processor.getClass())) { + return clazz.cast(processor); + } else if (processor instanceof MultiBatchProcessor multiProcessor) { + processors.addAll(multiProcessor.getBatchProcessors()); + } else if (processor instanceof BatchProcessorHolder holder) { + processors.add(holder.getProcessor()); + } + } while ((processor = processors.poll()) != null); + return null; + } catch (Throwable e) { + LOGGER.error("Error traversing processors", e); + return null; + } + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 780ba90e1..03b838de6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -30,6 +30,7 @@ import com.fastasyncworldedit.core.extent.ResettableExtent; import com.fastasyncworldedit.core.extent.SingleRegionExtent; import com.fastasyncworldedit.core.extent.SourceMaskExtent; import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard; +import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.function.SurfaceRegionFunction; @@ -55,6 +56,7 @@ import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.MaskTraverser; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.ProcessorTraverser; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.collection.BlockVector3Set; import com.fastasyncworldedit.core.util.task.RunnableVal; @@ -524,9 +526,15 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { * @return mask, may be null */ public Mask getMask() { - //FAWE start - ExtendTraverser & MaskingExtents - ExtentTraverser maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class); - return maskingExtent != null ? maskingExtent.get().getMask() : null; + //FAWE start - ExtentTraverser & MaskingExtents + MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class); + if (maskingExtent == null) { + ExtentTraverser processorExtent = + new ExtentTraverser<>(getExtent()).find(ExtentBatchProcessorHolder.class); + maskingExtent = + new ProcessorTraverser<>(processorExtent.get().getProcessor()).find(MaskingExtent.class); + } + return maskingExtent != null ? maskingExtent.getMask() : null; //FAWE end } @@ -609,23 +617,29 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { //FAWE start - use MaskingExtent & ExtentTraverser /** - * Set a mask. + * Set a mask. Combines with any existing masks, set null to clear existing masks. * * @param mask mask or null */ - public void setMask(Mask mask) { + public void setMask(@Nullable Mask mask) { if (mask == null) { mask = Masks.alwaysTrue(); } else { new MaskTraverser(mask).reset(this); } - ExtentTraverser maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class); - if (maskingExtent != null && maskingExtent.get() != null) { - Mask oldMask = maskingExtent.get().getMask(); + MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class); + if (maskingExtent == null && mask != Masks.alwaysTrue()) { + ExtentTraverser processorExtent = + new ExtentTraverser<>(getExtent()).find(ExtentBatchProcessorHolder.class); + maskingExtent = + new ProcessorTraverser<>(processorExtent.get().getProcessor()).find(MaskingExtent.class); + } + if (maskingExtent != null) { + Mask oldMask = maskingExtent.getMask(); if (oldMask instanceof ResettableMask) { ((ResettableMask) oldMask).reset(); } - maskingExtent.get().setMask(mask); + maskingExtent.setMask(mask); } else if (mask != Masks.alwaysTrue()) { addProcessor(new MaskingExtent(getExtent(), mask)); } From e6b1308590098db40ea346c8d2412d02dcdb35bc Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 15 Jul 2023 16:40:52 +0100 Subject: [PATCH 21/38] feat: synchronise disk clipboard loading and deletion to clipboardLock (#2342) - another possible help towards the OverlappingFileLockException --- .../com/fastasyncworldedit/core/Fawe.java | 1 - .../com/sk89q/worldedit/LocalSession.java | 57 +++++++++++++++++++ .../com/sk89q/worldedit/entity/Player.java | 35 +----------- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java index eb7eaf8ce..b4d2816c3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -449,7 +449,6 @@ public class Fawe { * @return Executor used for clipboard IO if clipboard on disk is enabled or null * @since 2.6.2 */ - @Nullable public KeyQueuedExecutorService getClipboardExecutor() { return this.clipboardExecutor; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index dfa29a2e3..3391bad80 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -23,8 +23,10 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.ResettableExtent; +import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.history.DiskStorageHistory; +import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismatchException; import com.fastasyncworldedit.core.internal.io.FaweInputStream; import com.fastasyncworldedit.core.internal.io.FaweOutputStream; import com.fastasyncworldedit.core.limit.FaweLimit; @@ -50,6 +52,8 @@ import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Locatable; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; @@ -93,6 +97,7 @@ import java.util.ListIterator; import java.util.Objects; import java.util.TimeZone; import java.util.UUID; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -877,6 +882,58 @@ public class LocalSession implements TextureHolder { } } } + + /** + * Load a clipboard from disk and into this localsession. Synchronises with other clipboard setting/getting to and from + * this session + * + * @param file Clipboard file to load + * @throws FaweClipboardVersionMismatchException in clipboard version mismatch (between saved and internal, expected, version) + * @throws ExecutionException if the computation threw an exception + * @throws InterruptedException if the current thread was interrupted while waiting + */ + public void loadClipboardFromDisk(File file) throws FaweClipboardVersionMismatchException, ExecutionException, + InterruptedException { + synchronized (clipboardLock) { + if (file.exists() && file.length() > 5) { + try { + if (getClipboard() != null) { + return; + } + } catch (EmptyClipboardException ignored) { + } + DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit( + uuid, + () -> DiskOptimizedClipboard.loadFromFile(file) + ).get(); + Clipboard clip = doc.toClipboard(); + ClipboardHolder holder = new ClipboardHolder(clip); + setClipboard(holder); + } + } + } + + public void deleteClipboardOnDisk() { + synchronized (clipboardLock) { + ClipboardHolder holder = getExistingClipboard(); + if (holder != null) { + for (Clipboard clipboard : holder.getClipboards()) { + DiskOptimizedClipboard doc; + if (clipboard instanceof DiskOptimizedClipboard) { + doc = (DiskOptimizedClipboard) clipboard; + } else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) { + doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent(); + } else { + continue; + } + Fawe.instance().getClipboardExecutor().submit(uuid, () -> { + doc.close(); // Ensure closed before deletion + doc.getFile().delete(); + }); + } + } + } + } //FAWE end /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java index 8f5b5cb53..277d755b1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -426,23 +426,7 @@ public interface Player extends Entity, Actor { cancel(true); LocalSession session = getSession(); if (Settings.settings().CLIPBOARD.USE_DISK && Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) { - ClipboardHolder holder = session.getExistingClipboard(); - if (holder != null) { - for (Clipboard clipboard : holder.getClipboards()) { - DiskOptimizedClipboard doc; - if (clipboard instanceof DiskOptimizedClipboard) { - doc = (DiskOptimizedClipboard) clipboard; - } else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) { - doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent(); - } else { - continue; - } - Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> { - doc.close(); // Ensure closed before deletion - doc.getFile().delete(); - }); - } - } + session.deleteClipboardOnDisk(); } else if (Settings.settings().CLIPBOARD.USE_DISK) { Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> session.setClipboard(null)); } else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) { @@ -464,22 +448,7 @@ public interface Player extends Entity, Actor { Settings.settings().PATHS.CLIPBOARD + File.separator + getUniqueId() + ".bd" ); try { - if (file.exists() && file.length() > 5) { - LocalSession session = getSession(); - try { - if (session.getClipboard() != null) { - return; - } - } catch (EmptyClipboardException ignored) { - } - DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit( - getUniqueId(), - () -> DiskOptimizedClipboard.loadFromFile(file) - ).get(); - Clipboard clip = doc.toClipboard(); - ClipboardHolder holder = new ClipboardHolder(clip); - session.setClipboard(holder); - } + getSession().loadClipboardFromDisk(file); } catch (FaweClipboardVersionMismatchException e) { print(e.getComponent()); } catch (RuntimeException e) { From b1e0ad4ef7f39a5d376b374180f69930927168c3 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 15 Jul 2023 16:41:04 +0100 Subject: [PATCH 22/38] feat: re-submit chunk load request after 10s after checking world is loaded (#2339) - #2332 makes it seem like paper forgets to load a chunk sometimes - resubmit chunk load request after a second to attempt to counter this --- .../PaperweightPlatformAdapter.java | 23 ++++++++++++++- .../v1_18_R2/PaperweightPlatformAdapter.java | 28 +++++++++++++++---- .../v1_19_R3/PaperweightPlatformAdapter.java | 26 ++++++++++++++--- .../v1_20_R1/PaperweightPlatformAdapter.java | 23 ++++++++++++++- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java index 69ab5fe9b..688a84a14 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java @@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -48,6 +49,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.gameevent.GameEventDispatcher; import net.minecraft.world.level.gameevent.GameEventListener; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; import sun.misc.Unsafe; @@ -61,6 +64,8 @@ import java.util.Locale; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.stream.Stream; @@ -91,6 +96,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static { try { fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l")); @@ -225,7 +232,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } return chunk.getHandle(); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java index 8e982a84e..dbf7f88f3 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java @@ -13,17 +13,16 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.IdMap; import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; @@ -41,7 +40,6 @@ import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.HashMapPalette; @@ -51,8 +49,8 @@ import net.minecraft.world.level.chunk.LinearPalette; import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; -import net.minecraft.world.level.gameevent.GameEventDispatcher; -import net.minecraft.world.level.gameevent.GameEventListener; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_18_R2.CraftChunk; import sun.misc.Unsafe; @@ -75,6 +73,8 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; public final class PaperweightPlatformAdapter extends NMSAdapter { @@ -106,6 +106,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static { try { fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); @@ -253,7 +255,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } return chunk.getHandle(); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java index 5af8f2806..d351bbacd 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java @@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -43,7 +44,6 @@ import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.HashMapPalette; @@ -54,6 +54,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; import sun.misc.Unsafe; @@ -61,7 +63,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -77,9 +78,10 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; -import static java.lang.invoke.MethodType.methodType; import static net.minecraft.core.registries.Registries.BIOME; public final class PaperweightPlatformAdapter extends NMSAdapter { @@ -111,6 +113,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static final boolean POST_CHUNK_REWRITE; private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES; @@ -287,7 +291,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } addTicket(serverLevel, chunkX, chunkZ); return (LevelChunk) chunk.getHandle(ChunkStatus.FULL); } catch (Throwable e) { diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java index f66e7dd6a..8cb8a8662 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java @@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -54,6 +55,8 @@ import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.SingleValuePalette; import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; import sun.misc.Unsafe; @@ -77,6 +80,8 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import static java.lang.invoke.MethodType.methodType; @@ -117,6 +122,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final Field fieldRemove; + private static final Logger LOGGER = LogManagerCompat.getLogger(); + static final boolean POST_CHUNK_REWRITE; private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; private static Field LEVEL_CHUNK_ENTITIES; @@ -307,7 +314,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk = (CraftChunk) future.get(); + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } addTicket(serverLevel, chunkX, chunkZ); return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); } catch (Throwable e) { From e0507e64402315f4829aa077cf30d8428ea6ab0e Mon Sep 17 00:00:00 2001 From: Chris Lang Date: Sat, 15 Jul 2023 00:22:45 -0700 Subject: [PATCH 23/38] Added cone generation command (#2251) * Added cone generation command * Fix formatting problems --------- Co-authored-by: Madeline Miller --- .../java/com/sk89q/worldedit/EditSession.java | 84 +++++++++++++++++++ .../worldedit/command/GenerationCommands.java | 43 ++++++++++ .../src/main/resources/lang/strings.json | 2 + 3 files changed, 129 insertions(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 03b838de6..f60a1cbf2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -2284,6 +2284,90 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { //FAWE end } + /** + * Makes a cone. + * + * @param pos Center of the cone + * @param block The block pattern to use + * @param radiusX The cone's largest north/south extent + * @param radiusZ The cone's largest east/west extent + * @param height The cone's up/down extent. If negative, extend downward. + * @param filled If false, only a shell will be generated. + * @param thickness The cone's wall thickness, if it's hollow. + * @return number of blocks changed + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int makeCone( + BlockVector3 pos, + Pattern block, + double radiusX, + double radiusZ, + int height, + boolean filled, + double thickness + ) throws MaxChangedBlocksException { + int affected = 0; + + final int ceilRadiusX = (int) Math.ceil(radiusX); + final int ceilRadiusZ = (int) Math.ceil(radiusZ); + + double rx2 = Math.pow(radiusX, 2); + double ry2 = Math.pow(height, 2); + double rz2 = Math.pow(radiusZ, 2); + + int cx = pos.getX(); + int cy = pos.getY(); + int cz = pos.getZ(); + + for (int y = 0; y < height; ++y) { + double ySquaredMinusHeightOverHeightSquared = Math.pow(y - height, 2) / ry2; + int yy = cy + y; + forX: + for (int x = 0; x <= ceilRadiusX; ++x) { + double xSquaredOverRadiusX = Math.pow(x, 2) / rx2; + int xx = cx + x; + forZ: + for (int z = 0; z <= ceilRadiusZ; ++z) { + int zz = cz + z; + double zSquaredOverRadiusZ = Math.pow(z, 2) / rz2; + double distanceFromOriginMinusHeightSquared = xSquaredOverRadiusX + zSquaredOverRadiusZ - ySquaredMinusHeightOverHeightSquared; + + if (distanceFromOriginMinusHeightSquared > 1) { + if (z == 0) { + break forX; + } + break forZ; + } + + if (!filled) { + double xNext = Math.pow(x + thickness, 2) / rx2 + zSquaredOverRadiusZ - ySquaredMinusHeightOverHeightSquared; + double yNext = xSquaredOverRadiusX + zSquaredOverRadiusZ - Math.pow(y + thickness - height, 2) / ry2; + double zNext = xSquaredOverRadiusX + Math.pow(z + thickness, 2) / rz2 - ySquaredMinusHeightOverHeightSquared; + if (xNext <= 0 && zNext <= 0 && (yNext <= 0 && y + thickness != height)) { + continue; + } + } + + if (distanceFromOriginMinusHeightSquared <= 0) { + if (setBlock(xx, yy, zz, block)) { + ++affected; + } + if (setBlock(xx, yy, zz, block)) { + ++affected; + } + if (setBlock(xx, yy, zz, block)) { + ++affected; + } + if (setBlock(xx, yy, zz, block)) { + ++affected; + } + } + } + } + } + return affected; + } + /** * Move the blocks in a region a certain direction. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index ca67ad132..dbd698b24 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -188,6 +188,49 @@ public class GenerationCommands { return affected; } + @Command( + name = "/cone", + desc = "Generates a cone." + ) + @CommandPermissions("worldedit.generation.cone") + @Logging(PLACEMENT) + public int cone(Actor actor, LocalSession session, EditSession editSession, + @Arg(desc = "The pattern of blocks to generate") + Pattern pattern, + @Arg(desc = "The radii of the cone. 1st is N/S, 2nd is E/W") + @Radii(2) + List radii, + @Arg(desc = "The height of the cone", def = "1") + int height, + @Switch(name = 'h', desc = "Make a hollow cone") + boolean hollow, + @Arg(desc = "Thickness of the hollow cone", def = "1") + double thickness + ) throws WorldEditException { + double radiusX; + double radiusZ; + switch (radii.size()) { + case 1 -> radiusX = radiusZ = Math.max(1, radii.get(0)); + case 2 -> { + radiusX = Math.max(1, radii.get(0)); + radiusZ = Math.max(1, radii.get(1)); + } + default -> { + actor.printError(Caption.of("worldedit.cone.invalid-radius")); + return 0; + } + } + + worldEdit.checkMaxRadius(radiusX); + worldEdit.checkMaxRadius(radiusZ); + worldEdit.checkMaxRadius(height); + + BlockVector3 pos = session.getPlacementPosition(actor); + int affected = editSession.makeCone(pos, pattern, radiusX, radiusZ, height, !hollow, thickness); + actor.printInfo(Caption.of("worldedit.cone.created", TextComponent.of(affected))); + return affected; + } + @Command( name = "/hsphere", desc = "Generates a hollow sphere." diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 790f7b6fa..5e922cdae 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -489,6 +489,8 @@ "worldedit.jumpto.none": "No block in sight (or too far away)!", "worldedit.up.obstructed": "You would hit something above you.", "worldedit.up.moved": "Woosh!", + "worldedit.cone.invalid-radius": "You must either specify 1 or 2 radius values.", + "worldedit.cone.created": "{0} blocks have been created.", "worldedit.cyl.invalid-radius": "You must either specify 1 or 2 radius values.", "worldedit.cyl.created": "{0} blocks have been created.", "worldedit.hcyl.thickness-too-large": "Thickness cannot be larger than x or z radii.", From 51670733262407ef964debde6fb84502fdefe700 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 17 Jul 2023 21:47:47 +0100 Subject: [PATCH 24/38] fix: regions not trimming Y correctly when restricted (#2363) --- .../java/com/fastasyncworldedit/core/queue/IBatchProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index 807a90fb4..5eeafe28e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -85,7 +85,7 @@ public interface IBatchProcessor { } for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) { if (set.hasSection(layer)) { - if (layer == minLayer) { + if (layer == maxLayer) { char[] arr = set.loadIfPresent(layer); if (arr != null) { int index = ((maxY + 1) & 15) << 8; From cdd546ee5e1a3f010b8daf8674c830925c197a03 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:55:59 +0100 Subject: [PATCH 25/38] fix: correct NBT for spawners (#2362) --- .../worldedit/blocks/MobSpawnerBlock.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java b/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java index 929354ab9..0420d0994 100644 --- a/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java +++ b/worldedit-core/src/legacy/java/com/sk89q/worldedit/blocks/MobSpawnerBlock.java @@ -134,15 +134,29 @@ public class MobSpawnerBlock extends BaseBlock { values.put("MaxNearbyEntities", new ShortTag(maxNearbyEntities)); values.put("RequiredPlayerRange", new ShortTag(requiredPlayerRange)); if (spawnData == null) { - values.put("SpawnData", new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))); + values.put( + "SpawnData", + new CompoundTag(ImmutableMap.of("entity", new CompoundTag(ImmutableMap.of("id", new StringTag(mobType))))) + ); } else { values.put("SpawnData", new CompoundTag(spawnData.getValue())); } if (spawnPotentials == null) { - values.put("SpawnPotentials", new ListTag(CompoundTag.class, ImmutableList.of( - new CompoundTag(ImmutableMap.of("Weight", new IntTag(1), "Entity", - new CompoundTag(ImmutableMap.of("id", new StringTag(mobType))) - ))))); + values.put( + "SpawnPotentials", + new ListTag( + CompoundTag.class, + ImmutableList.of(new CompoundTag(ImmutableMap.of( + "weight", + new IntTag(1), + "data", + new CompoundTag(ImmutableMap.of( + "entity", + new CompoundTag(ImmutableMap.of("id", new StringTag(mobType))) + )) + ))) + ) + ); } else { values.put("SpawnPotentials", new ListTag(CompoundTag.class, spawnPotentials.getValue())); } From f65c4743bda5d9cea218c7f7edbf324042a9dcf0 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:56:11 +0100 Subject: [PATCH 26/38] fix: make IdMask thread-safe (#2361) --- .../core/function/mask/IdMask.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java index e76c78a89..49c90183a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/IdMask.java @@ -5,9 +5,11 @@ import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; +import java.util.concurrent.atomic.AtomicInteger; + public class IdMask extends AbstractExtentMask implements ResettableMask { - private transient int id = -1; + private final AtomicInteger id = new AtomicInteger(-1); public IdMask(Extent extent) { super(extent); @@ -15,12 +17,9 @@ public class IdMask extends AbstractExtentMask implements ResettableMask { @Override public boolean test(Extent extent, BlockVector3 vector) { - if (id != -1) { - return extent.getBlock(vector).getInternalBlockTypeId() == id; - } else { - id = extent.getBlock(vector).getInternalBlockTypeId(); - return true; - } + int blockID = extent.getBlock(vector).getInternalBlockTypeId(); + int testId = id.compareAndExchange(-1, blockID); + return blockID == testId || testId == -1; } @Override @@ -30,12 +29,12 @@ public class IdMask extends AbstractExtentMask implements ResettableMask { @Override public void reset() { - this.id = -1; + this.id.set(-1); } @Override public Mask copy() { - return new IdMask(getExtent()); + return this; } @Override From fe1859e9d23caba0462593c8d26f90aa9edf9fe4 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:56:30 +0100 Subject: [PATCH 27/38] fix: do not retain clipboards that completed exceptionally (#2358) --- .../worldedit/command/ClipboardCommands.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 40ca530c8..e39bbc641 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweAPI; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Caption; @@ -28,6 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; +import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.ImgurUtility; @@ -160,7 +162,7 @@ public class ClipboardCommands { session.getPlacementPosition(actor)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); copy.setCopyingEntities(copyEntities); - createCopy(session, editSession, copyBiomes, mask, clipboard, copy); + createCopy(actor, session, editSession, copyBiomes, mask, clipboard, copy); copy.getStatusMessages().forEach(actor::print); //FAWE end @@ -271,7 +273,7 @@ public class ClipboardCommands { copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); copy.setCopyingEntities(copyEntities); copy.setRemovingEntities(true); - createCopy(session, editSession, copyBiomes, mask, clipboard, copy); + createCopy(actor, session, editSession, copyBiomes, mask, clipboard, copy); if (!actor.hasPermission("fawe.tips")) { actor.print(Caption.of("fawe.tips.tip.lazycut")); @@ -281,6 +283,7 @@ public class ClipboardCommands { } private void createCopy( + final Actor actor, final LocalSession session, final EditSession editSession, final boolean copyBiomes, @@ -311,9 +314,22 @@ public class ClipboardCommands { try { Operations.completeLegacy(copy); - } finally { - clipboard.flush(); + } catch (Exception e) { + DiskOptimizedClipboard doc; + if (clipboard instanceof DiskOptimizedClipboard) { + doc = (DiskOptimizedClipboard) clipboard; + } else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) { + doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent(); + } else { + throw e; + } + Fawe.instance().getClipboardExecutor().submit(actor.getUniqueId(), () -> { + clipboard.close(); + doc.getFile().delete(); + }); + throw e; } + clipboard.flush(); session.setClipboard(new ClipboardHolder(clipboard)); } From 0a8a4792143c692acc4bd3f64273b1767990f06e Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:57:29 +0100 Subject: [PATCH 28/38] fix: do not clash ChunkHolder recycling with processors that extend outside the chunk (#2353) --- .../ReentrantWrappedStampedLock.java | 119 ------------------ .../core/queue/IChunkExtent.java | 2 - .../SingleThreadQueueExtent.java | 33 ++--- .../implementation/chunk/ChunkHolder.java | 23 +++- 4 files changed, 34 insertions(+), 143 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/concurrent/ReentrantWrappedStampedLock.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/concurrent/ReentrantWrappedStampedLock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/concurrent/ReentrantWrappedStampedLock.java deleted file mode 100644 index b8e726620..000000000 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/concurrent/ReentrantWrappedStampedLock.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.fastasyncworldedit.core.concurrent; - -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.StampedLock; - -/** - * Allows for reentrant behaviour of a wrapped {@link StampedLock}. Will not count the number of times it is re-entered. - * - * @since 2.3.0 - */ -public class ReentrantWrappedStampedLock implements Lock { - - private final StampedLock parent = new StampedLock(); - private volatile Thread owner; - private volatile long stamp = 0; - - @Override - public void lock() { - if (Thread.currentThread() == owner) { - return; - } - stamp = parent.writeLock(); - owner = Thread.currentThread(); - } - - @Override - public void lockInterruptibly() throws InterruptedException { - if (Thread.currentThread() == owner) { - return; - } - stamp = parent.writeLockInterruptibly(); - owner = Thread.currentThread(); - } - - @Override - public boolean tryLock() { - if (Thread.currentThread() == owner) { - return true; - } - if (parent.isWriteLocked()) { - return false; - } - stamp = parent.writeLock(); - owner = Thread.currentThread(); - return true; - } - - @Override - public boolean tryLock(final long time, @NotNull final TimeUnit unit) throws InterruptedException { - if (Thread.currentThread() == owner) { - return true; - } - if (!parent.isWriteLocked()) { - stamp = parent.writeLock(); - owner = Thread.currentThread(); - return true; - } - stamp = parent.tryWriteLock(time, unit); - owner = Thread.currentThread(); - return false; - } - - @Override - public void unlock() { - if (owner != Thread.currentThread()) { - throw new IllegalCallerException("The lock should only be unlocked by the owning thread when a stamp is not supplied"); - } - unlock(stamp); - } - - @NotNull - @Override - public Condition newCondition() { - throw new UnsupportedOperationException("Conditions are not supported by StampedLock"); - } - - /** - * Retrieves the stamp associated with the current lock. 0 if the wrapped {@link StampedLock} is not write-locked. This method is - * thread-checking. - * - * @return lock stam[ or 0 if not locked. - * @throws IllegalCallerException if the {@link StampedLock} is write-locked and the calling thread is not the lock owner - * @since 2.3.0 - */ - public long getStampChecked() { - if (stamp != 0 && owner != Thread.currentThread()) { - throw new IllegalCallerException("The stamp should be be acquired by a thread that does not own the lock"); - } - return stamp; - } - - /** - * Unlock the wrapped {@link StampedLock} using the given stamp. This can be called by any thread. - * - * @param stamp Stamp to unlock with - * @throws IllegalMonitorStateException if the given stamp does not match the lock's stamp - * @since 2.3.0 - */ - public void unlock(final long stamp) { - parent.unlockWrite(stamp); - this.stamp = 0; - owner = null; - } - - /** - * Returns true if the lock is currently held. - * - * @return true if the lock is currently held. - * @since 2.3.0 - */ - public boolean isLocked() { - return owner == null && this.stamp == 0 && parent.isWriteLocked(); // Be verbose - } - -} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java index 63c793ebd..ec6162798 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java @@ -2,9 +2,7 @@ package com.fastasyncworldedit.core.queue; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.IntArrayTag; import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; import com.sk89q.jnbt.NBTUtils; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 59427c8ef..198782ee3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -83,17 +83,6 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen this.maxY = maxY; } - /** - * Safety check to ensure that the thread being used matches the one being initialized on. - Can - * be removed later - */ - private void checkThread() { - if (Thread.currentThread() != currentThread && currentThread != null) { - throw new UnsupportedOperationException( - "This class must be used from a single thread. Use multiple queues for concurrent operations"); - } - } - @Override public void enableQueue() { enabledQueue = true; @@ -154,10 +143,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen return; } if (!this.chunks.isEmpty()) { + getChunkLock.lock(); for (IChunk chunk : this.chunks.values()) { chunk.recycle(); } - getChunkLock.lock(); this.chunks.clear(); getChunkLock.unlock(); } @@ -233,9 +222,21 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen */ private > V submitUnchecked(IQueueChunk chunk) { if (chunk.isEmpty()) { - chunk.recycle(); - Future result = Futures.immediateFuture(null); - return (V) result; + if (chunk instanceof ChunkHolder holder) { + long age = holder.initAge(); + // Ensure we've given time for the chunk to be used - it was likely used for a reason! + if (age < 5) { + try { + Thread.sleep(5 - age); + } catch (InterruptedException ignored) { + } + } + } + if (chunk.isEmpty()) { + chunk.recycle(); + Future result = Futures.immediateFuture(null); + return (V) result; + } } if (Fawe.isMainThread()) { @@ -451,6 +452,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen @Override public synchronized void flush() { if (!chunks.isEmpty()) { + getChunkLock.lock(); if (MemUtil.isMemoryLimited()) { for (IQueueChunk chunk : chunks.values()) { final Future future = submitUnchecked(chunk); @@ -467,7 +469,6 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen } } } - getChunkLock.lock(); chunks.clear(); getChunkLock.unlock(); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 9172d8b3e..ec556d845 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -1,7 +1,6 @@ package com.fastasyncworldedit.core.queue.implementation.chunk; import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.concurrent.ReentrantWrappedStampedLock; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; @@ -26,6 +25,8 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * An abstract {@link IChunk} class that implements basic get/set blocks. @@ -43,7 +44,7 @@ public class ChunkHolder> implements IQueueChunk { return POOL.poll(); } - private final ReentrantWrappedStampedLock calledLock = new ReentrantWrappedStampedLock(); + private final Lock calledLock = new ReentrantLock(); private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting @@ -55,6 +56,7 @@ public class ChunkHolder> implements IQueueChunk { private int bitMask = -1; // Allow forceful setting of bitmask (for lighting) private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init. private boolean createCopy = false; + private long initTime = -1L; private ChunkHolder() { this.delegate = NULL; @@ -66,6 +68,7 @@ public class ChunkHolder> implements IQueueChunk { @Override public synchronized void recycle() { + calledLock.lock(); delegate = NULL; if (chunkSet != null) { chunkSet.recycle(); @@ -74,6 +77,11 @@ public class ChunkHolder> implements IQueueChunk { chunkExisting = null; extent = null; POOL.offer(this); + calledLock.unlock(); + } + + public long initAge() { + return System.currentTimeMillis() - initTime; } public synchronized IBlockDelegate getDelegate() { @@ -84,10 +92,10 @@ public class ChunkHolder> implements IQueueChunk { * If the chunk is currently being "called", this method will block until completed. */ private void checkAndWaitOnCalledLock() { - if (calledLock.isLocked()) { + if (!calledLock.tryLock()) { calledLock.lock(); - calledLock.unlock(); } + calledLock.unlock(); } @Override @@ -1024,6 +1032,7 @@ public class ChunkHolder> implements IQueueChunk { @Override public synchronized void init(IQueueExtent extent, int chunkX, int chunkZ) { + this.initTime = System.currentTimeMillis(); this.extent = extent; this.chunkX = chunkX; this.chunkZ = chunkZ; @@ -1040,14 +1049,15 @@ public class ChunkHolder> implements IQueueChunk { @Override public synchronized T call() { calledLock.lock(); - final long stamp = calledLock.getStampChecked(); if (chunkSet != null && !chunkSet.isEmpty()) { this.delegate = GET; chunkSet.setBitMask(bitMask); try { IChunkSet copy = chunkSet.createCopy(); chunkSet = null; - return this.call(copy, () -> calledLock.unlock(stamp)); + return this.call(copy, () -> { + // Do nothing + }); } catch (Throwable t) { calledLock.unlock(); throw t; @@ -1072,6 +1082,7 @@ public class ChunkHolder> implements IQueueChunk { } else { finalizer = finalize; } + calledLock.unlock(); return get.call(set, finalizer); } return null; From 18df87a4e8b2f3da815ecd3ce0e4a4ad36240f37 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:57:48 +0100 Subject: [PATCH 29/38] feat: implement a new "type swap" pattern (#2346) --- .../parser/pattern/TypeSwapPatternParser.java | 54 +++++++++ .../function/pattern/TypeSwapPattern.java | 103 ++++++++++++++++++ .../extension/factory/PatternFactory.java | 2 + 3 files changed, 159 insertions(+) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java new file mode 100644 index 000000000..5bf33d3a0 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java @@ -0,0 +1,54 @@ +package com.fastasyncworldedit.core.extension.factory.parser.pattern; + +import com.fastasyncworldedit.core.configuration.Caption; +import com.fastasyncworldedit.core.extension.factory.parser.RichParser; +import com.fastasyncworldedit.core.function.pattern.TypeSwapPattern; +import com.fastasyncworldedit.core.util.Permission; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.util.formatting.text.TextComponent; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.stream.Stream; + +public class TypeSwapPatternParser extends RichParser { + + private static final List SUGGESTIONS = List.of("oak", "spruce", "stone", "sandstone"); + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public TypeSwapPatternParser(WorldEdit worldEdit) { + super(worldEdit, "#typeswap", "#ts", "#swaptype"); + } + + @Override + public Stream getSuggestions(String argumentInput, int index) { + if (index > 2) { + return Stream.empty(); + } + return SUGGESTIONS.stream(); + } + + @Override + public Pattern parseFromInput(@Nonnull String[] input, ParserContext context) throws InputParseException { + if (input.length != 2) { + throw new InputParseException(Caption.of( + "fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[input][output] (e.g. " + getPrefix() + "[spruce][oak])") + )); + } + return new TypeSwapPattern( + context.requireExtent(), + input[0], + input[1], + Permission.hasPermission(context.requireActor(), "fawe.pattern.typeswap.regex") + ); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java new file mode 100644 index 000000000..89876f658 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java @@ -0,0 +1,103 @@ +package com.fastasyncworldedit.core.function.pattern; + +import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.util.StringMan; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.regex.Pattern; + +/** + * Pattern that replaces blocks based on their ID, matching for an "input" and replacing with an "output" string. The "input" + * string may be regex. Keeps as much of the block state as possible, excluding NBT data. + * + * @since TODO + */ +public class TypeSwapPattern extends AbstractExtentPattern { + + private static final Pattern SPLITTER = Pattern.compile("[|,]"); + + private final String inputString; + private final String outputString; + private final String[] inputs; + private Pattern inputPattern = null; + + /** + * Create a new instance + * + * @param extent extent to use + * @param inputString string to replace. May be regex. + * @param outputString string to replace with + * @param allowRegex if regex should be allowed for input string matching + * @since TODO + */ + public TypeSwapPattern(Extent extent, String inputString, String outputString, boolean allowRegex) { + super(extent); + this.inputString = inputString; + this.outputString = outputString; + if (!StringMan.isAlphanumericUnd(inputString)) { + if (allowRegex) { + this.inputPattern = Pattern.compile(inputString.replace(",", "|")); + inputs = null; + } else { + inputs = SPLITTER.split(inputString); + } + } else { + inputs = null; + } + } + + @Override + public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { + BlockState existing = get.getBlock(extent); + BlockState newBlock = getNewBlock(existing); + if (newBlock == null) { + return false; + } + return set.setBlock(extent, newBlock); + } + + @Override + public void applyBlock(final FilterBlock block) { + BlockState existing = block.getBlock(); + BlockState newState = getNewBlock(existing); + if (newState != null) { + block.setBlock(newState); + } + } + + @Override + public BaseBlock applyBlock(final BlockVector3 position) { + BaseBlock existing = position.getFullBlock(getExtent()); + BlockState newState = getNewBlock(existing.toBlockState()); + return newState == null ? existing : newState.toBaseBlock(); + } + + private BlockState getNewBlock(BlockState existing) { + String oldId = existing.getBlockType().getId(); + String newId = oldId; + if (inputPattern != null) { + newId = inputPattern.matcher(oldId).replaceAll(outputString); + } else if (inputs != null && inputs.length > 0) { + for (String input : inputs) { + newId = newId.replace(input, outputString); + } + } else { + newId = oldId.replace(inputString, outputString); + } + if (newId.equals(oldId)) { + return null; + } + BlockType newType = BlockTypes.get(newId); + if (newType == null) { + return null; + } + return newType.getDefaultState().withProperties(existing); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 420e58d2a..a76759833 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -42,6 +42,7 @@ import com.fastasyncworldedit.core.extension.factory.parser.pattern.OffsetPatter import com.fastasyncworldedit.core.extension.factory.parser.pattern.PerlinPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomFullClipboardPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomOffsetPatternParser; +import com.fastasyncworldedit.core.extension.factory.parser.pattern.TypeSwapPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RelativePatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RichPatternParser; @@ -133,6 +134,7 @@ public final class PatternFactory extends AbstractFactory { register(new SimplexPatternParser(worldEdit)); register(new SolidRandomOffsetPatternParser(worldEdit)); register(new SurfaceRandomOffsetPatternParser(worldEdit)); + register(new TypeSwapPatternParser(worldEdit)); register(new VoronoiPatternParser(worldEdit)); //FAWE end } From 9543adc776c1afcb81477e2113920f9746ea88a6 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:58:01 +0100 Subject: [PATCH 30/38] Implement async notify queue that submits to a KeyQueuedExecutorService (#2334) --- .../queue/implementation/QueueHandler.java | 26 ++++++ .../core/util/task/AsyncNotifyKeyedQueue.java | 81 +++++++++++++++++++ .../core/util/task/AsyncNotifyQueue.java | 16 +--- .../platform/AbstractNonPlayerActor.java | 7 +- .../platform/AbstractPlayerActor.java | 6 +- 5 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 7f604a277..bc7f9ddec 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -27,6 +27,7 @@ import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.Future; @@ -52,6 +53,7 @@ public abstract class QueueHandler implements Trimable, Runnable { null, false ); + /** * Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the * primary queue. They may be IO-bound tasks. @@ -508,4 +510,28 @@ public abstract class QueueHandler implements Trimable, Runnable { return result; } + /** + * Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their + * time utilising CPU. + *

+ * Internal API usage only. + * + * @since TODO + */ + public ExecutorService getForkJoinPoolPrimary() { + return forkJoinPoolPrimary; + } + + /** + * Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the + * primary queue. They may be IO-bound tasks. + *

+ * Internal API usage only. + * + * @since TODO + */ + public ExecutorService getForkJoinPoolSecondary() { + return forkJoinPoolSecondary; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java new file mode 100644 index 000000000..9df6b197c --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java @@ -0,0 +1,81 @@ +package com.fastasyncworldedit.core.util.task; + +import com.fastasyncworldedit.core.configuration.Settings; + +import java.io.Closeable; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +/** + * async queue that accepts a {@link Thread.UncaughtExceptionHandler} for exception handling per instance, delegating to a + * parent {@link KeyQueuedExecutorService}. + * + * @since TODO + */ +public class AsyncNotifyKeyedQueue implements Closeable { + + private static final KeyQueuedExecutorService QUEUE_SUBMISSIONS = new KeyQueuedExecutorService<>(new ForkJoinPool( + Settings.settings().QUEUE.PARALLEL_THREADS, + new FaweForkJoinWorkerThreadFactory("AsyncNotifyKeyedQueue - %s"), + null, + false + )); + + private final Thread.UncaughtExceptionHandler handler; + private final Supplier key; + private volatile boolean closed; + + /** + * New instance + * + * @param handler exception handler + * @param key supplier of UUID key + */ + public AsyncNotifyKeyedQueue(Thread.UncaughtExceptionHandler handler, Supplier key) { + this.handler = handler; + this.key = key; + } + + public Thread.UncaughtExceptionHandler getHandler() { + return handler; + } + + public Future run(Runnable task) { + return call(() -> { + task.run(); + return null; + }); + } + + public Future call(Callable task) { + Future[] self = new Future[1]; + Callable wrapped = () -> { + if (!closed) { + try { + return task.call(); + } catch (Throwable e) { + handler.uncaughtException(Thread.currentThread(), e); + } + } + if (self[0] != null) { + self[0].cancel(true); + } + return null; + }; + self[0] = QUEUE_SUBMISSIONS.submit(key.get(), wrapped); + return self[0]; + } + + @Override + public void close() { + closed = true; + } + + public boolean isClosed() { + return closed; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyQueue.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyQueue.java index 7cd91f87a..9cf9ddef2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyQueue.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyQueue.java @@ -1,13 +1,9 @@ package com.fastasyncworldedit.core.util.task; import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.configuration.Settings; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.Closeable; import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -15,13 +11,6 @@ import java.util.function.Supplier; public class AsyncNotifyQueue implements Closeable { - private static final ForkJoinPool QUEUE_SUBMISSIONS = new ForkJoinPool( - Settings.settings().QUEUE.PARALLEL_THREADS, - new FaweForkJoinWorkerThreadFactory("AsyncNotifyQueue - %s"), - null, - false - ); - private final Lock lock = new ReentrantLock(true); private final Thread.UncaughtExceptionHandler handler; private boolean closed; @@ -56,9 +45,6 @@ public class AsyncNotifyQueue implements Closeable { return task.call(); } catch (Throwable e) { handler.uncaughtException(Thread.currentThread(), e); - if (self[0] != null) { - self[0].cancel(true); - } } } } finally { @@ -70,7 +56,7 @@ public class AsyncNotifyQueue implements Closeable { } return null; }; - self[0] = QUEUE_SUBMISSIONS.submit(wrapped); + self[0] = Fawe.instance().getQueueHandler().async(wrapped); return self[0]; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java index 0b8909198..8337a7327 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java @@ -19,10 +19,9 @@ package com.sk89q.worldedit.extension.platform; -import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.util.TaskManager; -import com.fastasyncworldedit.core.util.task.AsyncNotifyQueue; +import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -68,7 +67,7 @@ public abstract class AbstractNonPlayerActor implements Actor { // Queue for async tasks private final AtomicInteger runningCount = new AtomicInteger(); - private final AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue((thread, throwable) -> { + private final AsyncNotifyKeyedQueue asyncNotifyQueue = new AsyncNotifyKeyedQueue((thread, throwable) -> { while (throwable.getCause() != null) { throwable = throwable.getCause(); } @@ -82,7 +81,7 @@ public abstract class AbstractNonPlayerActor implements Actor { throwable.printStackTrace(); } } - }); + }, this::getUniqueId); /** * Run a task either async, or on the current thread. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index 6adf7236c..ae4eb2144 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -25,7 +25,7 @@ import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.regions.FaweMaskManager; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.WEManager; -import com.fastasyncworldedit.core.util.task.AsyncNotifyQueue; +import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; @@ -81,7 +81,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { // Queue for async tasks private final AtomicInteger runningCount = new AtomicInteger(); - private final AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue( + private final AsyncNotifyKeyedQueue asyncNotifyQueue = new AsyncNotifyKeyedQueue( (thread, throwable) -> { while (throwable.getCause() != null) { throwable = throwable.getCause(); @@ -96,7 +96,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { throwable.printStackTrace(); } } - }); + }, this::getUniqueId); public AbstractPlayerActor(Map meta) { this.meta = meta; From 4bdcf9a510baf2df82e18f7e69ba3d5d85c8cfdb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 22 Jul 2023 10:00:15 +0000 Subject: [PATCH 31/38] Update dependency com.palmergames.bukkit.towny:towny to v0.99.5.4 (#2372) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cf04ae8f8..e1d802e37 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.99.2.7" +towny = "0.99.5.4" # Third party bstats = "3.0.2" From c91b477e2969239b35bfa84977a75e4fe0c1c8a3 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 22 Jul 2023 12:01:33 +0200 Subject: [PATCH 32/38] Update PlotSquared integration to v7 (#2075) Co-authored-by: dordsor21 --- .github/renovate.json | 2 -- buildSrc/src/main/kotlin/AdapterConfig.kt | 2 +- buildSrc/src/main/kotlin/CommonJavaConfig.kt | 2 +- gradle/libs.versions.toml | 3 +-- worldedit-bukkit/build.gradle.kts | 6 +++--- .../fastasyncworldedit/bukkit/FaweBukkit.java | 13 ++----------- .../FaweDelegateRegionManager.java | 10 ++++------ .../bukkit/regions/plotsquared/FaweTrim.java | 6 ++++-- .../regions/plotsquared/PlotSetBiome.java | 11 ++++++----- .../plotsquared/PlotSquaredFeature.java | 2 +- .../sk89q/wepif/TestOfflinePermissible.java | 19 ++++++++++++++++++- worldedit-core/build.gradle.kts | 5 ++--- 12 files changed, 43 insertions(+), 38 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 787e44030..526af5a60 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -19,8 +19,6 @@ "net.fabricmc:fabric-loader", "net.fabricmc.fabric-api:fabric-api", "com.github.luben:zstd-jni", - "net.kyori", - "net.kyori:adventure-nbt", "org.jetbrains.kotlin.jvm", "log4j" ], diff --git a/buildSrc/src/main/kotlin/AdapterConfig.kt b/buildSrc/src/main/kotlin/AdapterConfig.kt index 21cab4c73..6cad58e3b 100644 --- a/buildSrc/src/main/kotlin/AdapterConfig.kt +++ b/buildSrc/src/main/kotlin/AdapterConfig.kt @@ -15,7 +15,7 @@ fun Project.applyPaperweightAdapterConfiguration() { dependencies { "implementation"(project(":worldedit-bukkit")) - "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.31")) + "implementation"(platform("com.intellectualsites.bom:bom-newest:1.33")) } tasks.named("assemble") { diff --git a/buildSrc/src/main/kotlin/CommonJavaConfig.kt b/buildSrc/src/main/kotlin/CommonJavaConfig.kt index 759e45740..73389ce83 100644 --- a/buildSrc/src/main/kotlin/CommonJavaConfig.kt +++ b/buildSrc/src/main/kotlin/CommonJavaConfig.kt @@ -45,7 +45,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean "testImplementation"("org.mockito:mockito-core:5.1.1") "testImplementation"("org.mockito:mockito-junit-jupiter:5.1.1") "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.9.2") - "implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.31")) + "implementation"(platform("com.intellectualsites.bom:bom-newest:1.33")) } // Java 8 turns on doclint which we fail diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e1d802e37..9ff63fb98 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ towny = "0.99.5.4" bstats = "3.0.2" sparsebitset = "1.2" parallelgzip = "1.0.5" -adventure = "4.9.3" +adventure = "4.14.0" truezip = "6.8.4" auto-value = "1.10.2" findbugs = "3.0.2" @@ -30,7 +30,6 @@ jchronic = "0.2.4a" lz4-java = "1.8.0" lz4-stream = "1.0.0" ## Internal -adventure-text-minimessage = "4.2.0-SNAPSHOT" text-adapter = "3.0.6" text = "3.0.4" piston = "0.5.7" diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 9a9ffd86b..a6d31be03 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -101,8 +101,8 @@ dependencies { compileOnly(libs.griefdefender) { isTransitive = false } compileOnly(libs.residence) { isTransitive = false } compileOnly(libs.towny) { isTransitive = false } - compileOnly("com.plotsquared:PlotSquared-Bukkit") { isTransitive = false } - compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false } + compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } + compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false } // Third party implementation("io.papermc:paperlib") @@ -183,7 +183,7 @@ tasks.named("shadowJar") { include(dependency("org.lz4:lz4-java:1.8.0")) } relocate("net.kyori", "com.fastasyncworldedit.core.adventure") { - include(dependency("net.kyori:adventure-nbt:4.9.3")) + include(dependency("net.kyori:adventure-nbt:4.14.0")) } relocate("com.zaxxer", "com.fastasyncworldedit.core.math") { include(dependency("com.zaxxer:SparseBitSet:1.2")) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java index 2e25c8c56..90db4368c 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java @@ -325,18 +325,9 @@ public class FaweBukkit implements IFawe, Listener { if (plotSquared == null) { return; } - if (PlotSquared.get().getVersion().version[0] == 6) { + if (PlotSquared.get().getVersion().version[0] == 7) { WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature()); - LOGGER.info("Plugin 'PlotSquared' v6 found. Using it now."); - } else if (PlotSquared.get().getVersion().version[0] == 7) { - WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature()); - LOGGER.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - LOGGER.error("!! !!"); - LOGGER.error("!! ERROR: PlotSquared v7 found. This FAWE version does not support PlotSquared V7 !!"); - LOGGER.error("!! Follow the instructions when notified of v7 release candidates and use FAWE from !!"); - LOGGER.error("!! https://ci.athion.net/job/FastAsyncWorldEdit-Pull-Requests/view/change-requests/job/PR-2075/ !!"); - LOGGER.error("!! !!"); - LOGGER.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + LOGGER.info("Plugin 'PlotSquared' v7 found. Using it now."); } else { LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v6."); LOGGER.info("https://www.spigotmc.org/resources/77506/"); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java index 66a9ef369..4ef1a3d05 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java @@ -171,13 +171,11 @@ public class FaweDelegateRegionManager { .limitUnlimited() .changeSetNull() .build(); - File schematicFile = new File(hybridPlotWorld.getRoot(), "plot.schem"); + File schematicFile = new File(hybridPlotWorld.getSchematicRoot(), "plot.schem"); if (!schematicFile.exists()) { - schematicFile = new File(hybridPlotWorld.getRoot(), "plot.schematic"); + schematicFile = new File(hybridPlotWorld.getSchematicRoot(), "plot.schematic"); } - BlockVector3 to = plot.getBottomAbs().getBlockVector3().withY(Settings.Schematics.PASTE_ON_TOP - ? hybridPlotWorld.SCHEM_Y - : hybridPlotWorld.getMinBuildHeight()); + BlockVector3 to = plot.getBottomAbs().getBlockVector3().withY(hybridPlotWorld.getPlotYStart()); try { Clipboard clip = ClipboardFormats .findByFile(schematicFile) @@ -215,7 +213,7 @@ public class FaweDelegateRegionManager { ) { TaskManager.taskManager().async(() -> { synchronized (FaweDelegateRegionManager.class) { - //todo because of the following code this should proably be in the Bukkit module + //todo because of the following code this should probably be in the Bukkit module World pos1World = BukkitAdapter.adapt(getWorld(pos1.getWorldName())); World pos3World = BukkitAdapter.adapt(getWorld(swapPos.getWorldName())); EditSession sessionA = WorldEdit.getInstance().newEditSessionBuilder().world(pos1World) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweTrim.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweTrim.java index 8bfab4939..665fa47e4 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweTrim.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweTrim.java @@ -1,13 +1,15 @@ package com.fastasyncworldedit.bukkit.regions.plotsquared; import com.fastasyncworldedit.core.util.TaskManager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.command.CommandCategory; import com.plotsquared.core.command.CommandDeclaration; import com.plotsquared.core.command.RequiredType; import com.plotsquared.core.command.SubCommand; import com.plotsquared.core.configuration.caption.StaticCaption; -import com.plotsquared.core.configuration.caption.Templates; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.player.PlotPlayer; @@ -33,7 +35,7 @@ public class FaweTrim extends SubCommand { return false; } if (!PlotSquared.platform().worldUtil().isWorld(strings[0])) { - plotPlayer.sendMessage(TranslatableCaption.of("errors.not_valid_plot_world"), Templates.of("value", strings[0])); + plotPlayer.sendMessage(TranslatableCaption.of("errors.not_valid_plot_world"), TagResolver.resolver("value", Tag.inserting(Component.text(strings[0])))); return false; } ran = true; diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSetBiome.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSetBiome.java index cb007bfb3..ef903b716 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSetBiome.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSetBiome.java @@ -6,11 +6,9 @@ import com.plotsquared.core.command.CommandCategory; import com.plotsquared.core.command.CommandDeclaration; import com.plotsquared.core.command.MainCommand; import com.plotsquared.core.command.RequiredType; -import com.plotsquared.core.configuration.caption.Templates; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; -import com.plotsquared.core.util.Permissions; import com.plotsquared.core.util.StringMan; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; @@ -24,6 +22,9 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.Biomes; import com.sk89q.worldedit.world.registry.BiomeRegistry; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Bukkit; import java.util.Collection; @@ -56,7 +57,7 @@ public class PlotSetBiome extends Command { ) throws CommandException { final Plot plot = check(player.getCurrentPlot(), TranslatableCaption.of("errors.not_in_plot")); checkTrue( - plot.isOwner(player.getUUID()) || Permissions.hasPermission(player, "plots.admin.command.generatebiome"), + plot.isOwner(player.getUUID()) || player.hasPermission("plots.admin.command.generatebiome"), TranslatableCaption.of("permission.no_plot_perms") ); if (plot.getRunning() != 0) { @@ -64,7 +65,7 @@ public class PlotSetBiome extends Command { return null; } checkTrue(args.length == 1, TranslatableCaption.of("commandconfig.command_syntax"), - Templates.of("value", getUsage()) + TagResolver.resolver("value", Tag.inserting(Component.text(getUsage()))) ); final Set regions = plot.getRegions(); BiomeRegistry biomeRegistry = @@ -80,7 +81,7 @@ public class PlotSetBiome extends Command { player.sendMessage(TranslatableCaption.of("biome.need_biome")); player.sendMessage( TranslatableCaption.of("commandconfig.subcommand_set_options_header"), - Templates.of("values", biomes) + TagResolver.resolver("value", Tag.inserting(Component.text(biomes))) ); return CompletableFuture.completedFuture(false); } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java index a34fac51f..e1ac20617 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java @@ -42,7 +42,7 @@ public class PlotSquaredFeature extends FaweMaskManager { if (Settings.FAWE_Components.FAWE_HOOK) { Settings.Enabled_Components.WORLDEDIT_RESTRICTIONS = false; if (Settings.PLATFORM.toLowerCase(Locale.ROOT).startsWith("bukkit")) { - new FaweTrim(); + // new FaweTrim(); } // TODO: revisit this later on /* diff --git a/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java b/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java index 9ef3d3894..981812e1b 100644 --- a/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java +++ b/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java @@ -19,6 +19,8 @@ package com.sk89q.wepif; +import com.destroystokyo.paper.profile.PlayerProfile; +import org.bukkit.BanEntry; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; @@ -30,10 +32,11 @@ import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.Plugin; -import org.bukkit.profile.PlayerProfile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.annotation.Nonnull; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; @@ -158,6 +161,15 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public @Nullable BanEntry ban( + @Nullable final String reason, + @Nullable final Date expires, + @Nullable final String source + ) { + return null; + } + @Override public boolean isWhitelisted() { throw new UnsupportedOperationException("Not supported yet."); @@ -323,4 +335,9 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible { } + @Override + public @Nullable Location getLastDeathLocation() { + return null; + } + } diff --git a/worldedit-core/build.gradle.kts b/worldedit-core/build.gradle.kts index c9b34d5b1..4e624bd3b 100644 --- a/worldedit-core/build.gradle.kts +++ b/worldedit-core/build.gradle.kts @@ -28,14 +28,13 @@ dependencies { implementation("com.google.code.gson:gson") // Platform expectations - // TODO update bom-newest - implementation("org.yaml:snakeyaml:2.0") + implementation("org.yaml:snakeyaml") // Logging implementation("org.apache.logging.log4j:log4j-api") // Plugins - compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false } + compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false } // ensure this is on the classpath for the AP annotationProcessor(libs.guava) From 34a98a03c1c43440ea93c6042763b7d1bf5f3aa0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 22 Jul 2023 10:02:22 +0000 Subject: [PATCH 33/38] Update dependency gradle to v8.2.1 (#2373) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f495dfe..9f4197d5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From f58f00e97f30c72f0834d37b2aa0e0b1260af8ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 22 Jul 2023 12:21:08 +0200 Subject: [PATCH 34/38] Update plugin com.modrinth.minotaur to v2.8.2 (#2374) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- worldedit-bukkit/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index a6d31be03..1962ff078 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -3,7 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation plugins { `java-library` - id("com.modrinth.minotaur") version "2.8.1" + id("com.modrinth.minotaur") version "2.8.2" } project.description = "Bukkit" From 37bfe426bc2661686f146ba5224ad86f4c4467c8 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 22 Jul 2023 12:27:54 +0200 Subject: [PATCH 35/38] Release 2.7.0 --- build.gradle.kts | 4 ++-- .../extent/processor/EntityInBlockRemovingProcessor.java | 2 +- .../core/function/pattern/TypeSwapPattern.java | 4 ++-- .../core/internal/exception/FaweException.java | 4 ++-- .../core/queue/implementation/QueueHandler.java | 4 ++-- .../com/fastasyncworldedit/core/regions/FaweMask.java | 2 +- .../fastasyncworldedit/core/regions/FaweMaskManager.java | 2 +- .../com/fastasyncworldedit/core/util/TaskManager.java | 8 ++++---- .../core/util/task/AsyncNotifyKeyedQueue.java | 2 +- .../java/com/sk89q/worldedit/world/block/BlockType.java | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index de0d714cc..a0930e8ec 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.6.5") +var rootVersion by extra("2.7.0") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s-%s", rootVersion, buildNumber) +version = String.format("%s", rootVersion) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java index 1ffea1266..58baf8633 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/EntityInBlockRemovingProcessor.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable; /** * Processor that removes existing entities that would not be in air after the edit * - * @since TODO + * @since 2.7.0 */ public class EntityInBlockRemovingProcessor implements IBatchProcessor { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java index 89876f658..efc122b5d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java @@ -16,7 +16,7 @@ import java.util.regex.Pattern; * Pattern that replaces blocks based on their ID, matching for an "input" and replacing with an "output" string. The "input" * string may be regex. Keeps as much of the block state as possible, excluding NBT data. * - * @since TODO + * @since 2.7.0 */ public class TypeSwapPattern extends AbstractExtentPattern { @@ -34,7 +34,7 @@ public class TypeSwapPattern extends AbstractExtentPattern { * @param inputString string to replace. May be regex. * @param outputString string to replace with * @param allowRegex if regex should be allowed for input string matching - * @since TODO + * @since 2.7.0 */ public TypeSwapPattern(Extent extent, String inputString, String outputString, boolean allowRegex) { super(extent); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/exception/FaweException.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/exception/FaweException.java index fd74ac5de..7a71b831b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/exception/FaweException.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/internal/exception/FaweException.java @@ -41,7 +41,7 @@ public class FaweException extends RuntimeException { * New instance of a given {@link FaweException.Type} * * @param ignorable if an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent} - * @since TODO + * @since 2.7.0 */ public FaweException(Component reason, Type type, boolean ignorable) { this.message = reason; @@ -70,7 +70,7 @@ public class FaweException extends RuntimeException { /** * If an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent} * - * @since TODO + * @since 2.7.0 */ public boolean ignorable() { return ignorable; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index bc7f9ddec..014b94fce 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -516,7 +516,7 @@ public abstract class QueueHandler implements Trimable, Runnable { *

* Internal API usage only. * - * @since TODO + * @since 2.7.0 */ public ExecutorService getForkJoinPoolPrimary() { return forkJoinPoolPrimary; @@ -528,7 +528,7 @@ public abstract class QueueHandler implements Trimable, Runnable { *

* Internal API usage only. * - * @since TODO + * @since 2.7.0 */ public ExecutorService getForkJoinPoolSecondary() { return forkJoinPoolSecondary; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java index 478ac90c9..f50c1a67e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMask.java @@ -38,7 +38,7 @@ public class FaweMask implements IDelegateRegion { * @param type type of mask * @param notify if the player should be notified * @return if still valid - * @since TODO + * @since 2.7.0 */ public boolean isValid(Player player, FaweMaskManager.MaskType type, boolean notify) { return isValid(player, type); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMaskManager.java index c52cc17e7..29136a178 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMaskManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FaweMaskManager.java @@ -30,7 +30,7 @@ public abstract class FaweMaskManager { /** * Get a {@link FaweMask} for the given player and {@link MaskType}. If isWhitelist is false, will return a "blacklist" mask. * - * @since TODO + * @since 2.7.0 */ public FaweMask getMask(final Player player, MaskType type, boolean isWhitelist, boolean notify) { return getMask(player, type, isWhitelist); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java index 65025bcd0..176e02673 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java @@ -91,7 +91,7 @@ public abstract class TaskManager { * * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.7.0") public void parallel(Collection runables) { for (Runnable run : runables) { pool.submit(run); @@ -106,7 +106,7 @@ public abstract class TaskManager { * @param numThreads number of threads (null = config.yml parallel threads) * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.7.0") public void parallel(Collection runnables, @Nullable Integer numThreads) { if (runnables == null) { return; @@ -278,7 +278,7 @@ public abstract class TaskManager { /** * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.7.0") public void wait(AtomicBoolean running, int timeout) { try { long start = System.currentTimeMillis(); @@ -299,7 +299,7 @@ public abstract class TaskManager { /** * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.7.0") public void notify(AtomicBoolean running) { running.set(false); synchronized (running) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java index 9df6b197c..cb6747cd2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/AsyncNotifyKeyedQueue.java @@ -13,7 +13,7 @@ import java.util.function.Supplier; * async queue that accepts a {@link Thread.UncaughtExceptionHandler} for exception handling per instance, delegating to a * parent {@link KeyQueuedExecutorService}. * - * @since TODO + * @since 2.7.0 */ public class AsyncNotifyKeyedQueue implements Closeable { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java index e5a466e29..7762ee532 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java @@ -86,7 +86,7 @@ public class BlockType implements Keyed, Pattern { * a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use * {@link BlockTypes#get(String)} instead. */ - @Deprecated(since = "TODO") + @Deprecated(since = "2.7.0") //FAWE end public BlockType(String id) { this(id, null); @@ -98,7 +98,7 @@ public class BlockType implements Keyed, Pattern { * a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use * {@link BlockTypes#get(String)} instead. */ - @Deprecated(since = "TODO") + @Deprecated(since = "2.7.0") //FAWE end public BlockType(String id, Function values) { // If it has no namespace, assume minecraft. From 006ccd6887d06b2de5e96ab15f9f595f5b76d9c8 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 22 Jul 2023 12:44:37 +0200 Subject: [PATCH 36/38] Back to snapshot for development --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a0930e8ec..3f6169bb7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.7.0") +var rootVersion by extra("2.7.1") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s", rootVersion) +version = String.format("%s-%s", rootVersion, buildNumber) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From 2f6de996e3d3cfc542e10b01fc379e1450450623 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 22 Jul 2023 13:14:01 +0100 Subject: [PATCH 37/38] fix: processorExtent can be null in editsession - fixes #2375 --- .../fastasyncworldedit/core/util/ExtentTraverser.java | 3 +++ .../src/main/java/com/sk89q/worldedit/EditSession.java | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java index b4822863c..e7038deaa 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java @@ -4,6 +4,7 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.lang.reflect.Field; public class ExtentTraverser { @@ -24,6 +25,7 @@ public class ExtentTraverser { return root != null; } + @Nullable public T get() { return root; } @@ -50,6 +52,7 @@ public class ExtentTraverser { } @SuppressWarnings("unchecked") + @Nullable public U findAndGet(Class clazz) { ExtentTraverser traverser = find(clazz); return (traverser != null) ? (U) traverser.get() : null; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index f60a1cbf2..983292ca5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -529,10 +529,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { //FAWE start - ExtentTraverser & MaskingExtents MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class); if (maskingExtent == null) { - ExtentTraverser processorExtent = - new ExtentTraverser<>(getExtent()).find(ExtentBatchProcessorHolder.class); - maskingExtent = - new ProcessorTraverser<>(processorExtent.get().getProcessor()).find(MaskingExtent.class); + ExtentBatchProcessorHolder processorExtent = + new ExtentTraverser<>(getExtent()).findAndGet(ExtentBatchProcessorHolder.class); + if (processorExtent != null) { + maskingExtent = + new ProcessorTraverser<>(processorExtent.getProcessor()).find(MaskingExtent.class); + } } return maskingExtent != null ? maskingExtent.getMask() : null; //FAWE end From 48be6ac94bb559d563eab6f2e5b92b807f2e089e Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 22 Jul 2023 17:44:05 +0100 Subject: [PATCH 38/38] fix: processorExtent can be null when setting masks too - fixes #2379 --- .../src/main/java/com/sk89q/worldedit/EditSession.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 983292ca5..ecf859896 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -631,10 +631,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class); if (maskingExtent == null && mask != Masks.alwaysTrue()) { - ExtentTraverser processorExtent = - new ExtentTraverser<>(getExtent()).find(ExtentBatchProcessorHolder.class); - maskingExtent = - new ProcessorTraverser<>(processorExtent.get().getProcessor()).find(MaskingExtent.class); + ExtentBatchProcessorHolder processorExtent = + new ExtentTraverser<>(getExtent()).findAndGet(ExtentBatchProcessorHolder.class); + if (processorExtent != null) { + maskingExtent = + new ProcessorTraverser<>(processorExtent.getProcessor()).find(MaskingExtent.class); + } } if (maskingExtent != null) { Mask oldMask = maskingExtent.getMask();