From 8cfb8cb322e46a613df0909bcf5cc6995c614375 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 22 Apr 2023 10:45:02 +0100 Subject: [PATCH 01/65] chore: add chunk tickets to all chunks accessed (#2163) - This isn't necessarily targeting any fix, but I think we should be ensuring a ticket is being added to chunks "access asynchronously", as done by the getChunkAtAsync method --- .../fawe/v1_17_R1_2/PaperweightPlatformAdapter.java | 11 +++++++++++ .../fawe/v1_18_R2/PaperweightPlatformAdapter.java | 11 +++++++++++ .../fawe/v1_19_R1/PaperweightPlatformAdapter.java | 11 +++++++++++ .../fawe/v1_19_R2/PaperweightPlatformAdapter.java | 11 +++++++++++ .../fawe/v1_19_R3/PaperweightPlatformAdapter.java | 12 ++++++++++++ 5 files changed, 56 insertions(+) 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 3ea9df730..69ab5fe9b 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 @@ -29,7 +29,9 @@ import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.util.BitStorage; +import net.minecraft.util.Unit; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelAccessor; @@ -209,10 +211,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } // Avoid "async" methods from the main thread. @@ -230,6 +234,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } + private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + // Ensure chunk is definitely loaded before applying a ticket + net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; try { 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 9de310b96..8e982a84e 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 @@ -29,9 +29,11 @@ import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.util.BitStorage; import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.ThreadingDetector; +import net.minecraft.util.Unit; import net.minecraft.util.ZeroBitStorage; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -237,10 +239,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } // Avoid "async" methods from the main thread. @@ -258,6 +262,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } + private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + // Ensure chunk is definitely loaded before applying a ticket + net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; try { diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java index 0423a03a7..2fcb72225 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java @@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.util.BitStorage; import net.minecraft.util.ExceptionCollector; import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.ThreadingDetector; +import net.minecraft.util.Unit; import net.minecraft.util.ZeroBitStorage; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -270,10 +272,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } // Avoid "async" methods from the main thread. @@ -291,6 +295,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } + private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; try { diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java index 1a4d3e523..5f8e39940 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java @@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.util.BitStorage; import net.minecraft.util.ExceptionCollector; import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.ThreadingDetector; +import net.minecraft.util.Unit; import net.minecraft.util.ZeroBitStorage; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -267,10 +269,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } // Avoid "async" methods from the main thread. @@ -288,6 +292,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } + private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; try { 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 33be73c69..11dadaf1e 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 @@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; import net.minecraft.util.BitStorage; import net.minecraft.util.ExceptionCollector; import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.ThreadingDetector; +import net.minecraft.util.Unit; import net.minecraft.util.ZeroBitStorage; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -292,10 +294,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); return nmsChunk; } // Avoid "async" methods from the main thread. @@ -305,6 +309,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { CraftChunk chunk = (CraftChunk) future.get(); + addTicket(serverLevel, chunkX, chunkZ); return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); } catch (Throwable e) { e.printStackTrace(); @@ -313,6 +318,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); } + private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; try { From 488a2e5de4aaf2545cede0a44e9e6dd2e9e6f352 Mon Sep 17 00:00:00 2001 From: 360shvit <54532500+360shvit@users.noreply.github.com> Date: Sat, 22 Apr 2023 18:48:59 +0200 Subject: [PATCH 02/65] Add an explanation to Disallowed-Blocks Comment (#2186) Improve user experience by adding an explanation to Disallowed-Blocks Configblock (#2183) --- .../java/com/fastasyncworldedit/core/configuration/Settings.java | 1 + 1 file changed, 1 insertion(+) 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 c2cb7704d..502e9dbf3 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 @@ -394,6 +394,7 @@ public class Settings extends Config { "Where block properties are specified, any blockstate with the property will be disallowed (e.g. all directions", "of a waterlogged fence). For blocking/remapping of all occurrences of a property like waterlogged, see", "remap-properties below.", + "To generate a blank list, substitute the default content with a set of square brackets [] instead.", "Example block property blocking:", " - \"minecraft:conduit[waterlogged=true]\"", " - \"minecraft:piston[extended=false,facing=west]\"", From c86dfe45df049d5df7285df68ecfa03a2117af51 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sun, 23 Apr 2023 22:15:37 +0200 Subject: [PATCH 03/65] Ensure regen step not running on the main thread (#2185) --- .../bukkit/adapter/Regenerator.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index 8f3b4d118..c95fbc580 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -253,10 +253,13 @@ public abstract class Regenerator { + for (long xz : coords) { + chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); + } + }).get(); // wait until finished this step } } From abe120291f99a505ec68ec2d8dd902f2e53bda16 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 23 Apr 2023 22:47:30 +0200 Subject: [PATCH 04/65] Release 2.6.1 --- .github/workflows/announce-release-on-discord.yml | 2 +- build.gradle.kts | 4 ++-- buildSrc/build.gradle.kts | 2 +- worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts | 2 +- worldedit-bukkit/build.gradle.kts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/announce-release-on-discord.yml b/.github/workflows/announce-release-on-discord.yml index 810366540..77fa89732 100644 --- a/.github/workflows/announce-release-on-discord.yml +++ b/.github/workflows/announce-release-on-discord.yml @@ -13,7 +13,7 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} DISCORD_USERNAME: FastAsyncWorldEdit Release DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png - uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # ratchet:Ilshidur/action-discord@0.3.2 + uses: Ilshidur/action-discord@0.3.2 with: args: | "<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>" diff --git a/build.gradle.kts b/build.gradle.kts index 5bbde35da..2167d306b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.6.0") +var rootVersion by extra("2.6.1") 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/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 15237a86e..d664f0c61 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { implementation(gradleApi()) implementation("org.ajoberstar.grgit:grgit-gradle:5.0.0") implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") - implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.3") + implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.4") } kotlin { diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts index 4d1c1ada5..cf00f80f2 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts @@ -9,6 +9,6 @@ repositories { } dependencies { - paperDevBundle("1.19.4-R0.1-20230412.010331-64") + paperDevBundle("1.19.4-R0.1-20230423.020222-72") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 41242611f..a6c5147ad 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.+" + id("com.modrinth.minotaur") version "2.7.5" } project.description = "Bukkit" From 9f3c909254994d53c6574d0d27031406166fd399 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 23 Apr 2023 23:01:03 +0200 Subject: [PATCH 05/65] 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 2167d306b..a58e32833 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.6.1") +var rootVersion by extra("2.6.2") 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 ffe704d890927f613d62f4aff86997ceece43827 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:03:49 +0200 Subject: [PATCH 06/65] Update dependency org.mockito:mockito-core to v5.3.1 (#2204) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- worldedit-sponge/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d450d7ad9..4e852c642 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,7 @@ text = "3.0.4" piston = "0.5.7" # Tests -mockito = "5.2.0" +mockito = "5.3.1" # Gradle plugins pluginyml = "0.5.3" diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index 76828b320..e895e2989 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.2.0") + testImplementation("org.mockito:mockito-core:5.3.1") } <<<<<<< HEAD From 909b7d27b88d1850c243ee6c3ec4861b65133c81 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:04:49 +0200 Subject: [PATCH 07/65] Update dependency org.ajoberstar.grgit:grgit-gradle to v5.2.0 (#2203) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index d664f0c61..1f1aded61 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -22,7 +22,7 @@ val properties = Properties().also { props -> dependencies { implementation(gradleApi()) - implementation("org.ajoberstar.grgit:grgit-gradle:5.0.0") + implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0") implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.4") } From 4779bd4916e893d4a6e2bab719249dd3cb1d97d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:20:24 +0200 Subject: [PATCH 08/65] Update plugin xyz.jpenilla.run-paper to v2.1.0 (#2205) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index a58e32833..2879f6107 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer plugins { id("io.github.gradle-nexus.publish-plugin") version "1.3.0" - id("xyz.jpenilla.run-paper") version "2.0.1" + id("xyz.jpenilla.run-paper") version "2.1.0" } if (!File("$rootDir/.git").exists()) { From 950a3f9430eec5e0a3ab82696874c8bc5a65a27f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:22:18 +0200 Subject: [PATCH 09/65] Update dependency com.github.TownyAdvanced:Towny to v0.99.0.5 (#2202) 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 4e852c642..6d1869213 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.98.4.18" +towny = "0.99.0.5" # Third party bstats = "3.0.2" From 92a6ff5f88592714b364d5f95f98a7d6b87aa9bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:22:27 +0200 Subject: [PATCH 10/65] Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.5 (#2201) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 1f1aded61..461e9deaa 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { implementation(gradleApi()) implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0") implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") - implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.4") + implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5") } kotlin { From e08720b31093cfe6ecded6e52888e5ac6147000e Mon Sep 17 00:00:00 2001 From: Phillipp Glanz Date: Thu, 4 May 2023 18:30:10 +0200 Subject: [PATCH 11/65] Improve git ignore (#2206) * Exclude run-server for all version * Exclude run-server for all version * Exclude run-server for all version * Exclude run-server for all version * Exclude run-server for all version * Remove unsless entry --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 14ba6416a..0a9d6020d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ worldedit-core/src/main/resources/lang/* /worldedit-core/.factorypath .DS_Store +### Run server ignore +run-* From 2987550e9b4ee0ab1d54fdfa5320773f6060abd2 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Thu, 4 May 2023 18:33:04 +0200 Subject: [PATCH 12/65] Actually delegate in AbstractDelegateExtent (#2196) * actually delegate in AbstractDelegateExtent * exclude enableHistory method --- .../extent/AbstractDelegateExtent.java | 296 +++++++++++++++++- .../fastasyncworldedit/ArchitecturalTest.java | 30 ++ 2 files changed, 322 insertions(+), 4 deletions(-) create mode 100644 worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 9dfd373ea..06b10b62b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -23,29 +23,43 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.NullExtent; +import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.function.generator.GenBase; +import com.fastasyncworldedit.core.function.generator.Resource; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.OperationQueue; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.ClipboardHolder; +import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.List; +import java.util.Set; import java.util.UUID; import static com.google.common.base.Preconditions.checkNotNull; @@ -53,9 +67,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * A base class for {@link Extent}s that merely passes extents onto another. */ -//FAWE start - made none abstract -public class AbstractDelegateExtent implements Extent { -//FAWE end +public abstract class AbstractDelegateExtent implements Extent { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -68,7 +80,7 @@ public class AbstractDelegateExtent implements Extent { * * @param extent the extent */ - public AbstractDelegateExtent(Extent extent) { + protected AbstractDelegateExtent(Extent extent) { checkNotNull(extent); this.extent = extent; } @@ -115,6 +127,12 @@ public class AbstractDelegateExtent implements Extent { //FAWE end } + @SuppressWarnings("deprecation") + @Override + public BiomeType getBiome(final BlockVector2 position) { + return extent.getBiome(position); + } + @Override public BlockVector3 getMinimumPoint() { return extent.getMinimumPoint(); @@ -225,11 +243,220 @@ public class AbstractDelegateExtent implements Extent { } } + @Override + public boolean isWorld() { + return extent.isWorld(); + } + + @Override + public boolean regenerateChunk( + final int x, + final int z, + @Nullable final BiomeType type, + @Nullable final Long seed + ) { + return extent.regenerateChunk(x, z, type, seed); + } + + @Override + public int getHighestTerrainBlock(final int x, final int z, final int minY, final int maxY) { + return extent.getHighestTerrainBlock(x, z, minY, maxY); + } + + @Override + public int getHighestTerrainBlock(final int x, final int z, final int minY, final int maxY, final Mask filter) { + return extent.getHighestTerrainBlock(x, z, minY, maxY, filter); + } + + @Override + public int getNearestSurfaceLayer(final int x, final int z, final int y, final int minY, final int maxY) { + return extent.getNearestSurfaceLayer(x, z, y, minY, maxY); + } + + @Override + public int getNearestSurfaceTerrainBlock( + final int x, + final int z, + final int y, + final int minY, + final int maxY, + final int failedMin, + final int failedMax, + final Mask mask + ) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); + } + + @Override + public int getNearestSurfaceTerrainBlock( + final int x, + final int z, + final int y, + final int minY, + final int maxY, + final boolean ignoreAir + ) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); + } + + @Override + public int getNearestSurfaceTerrainBlock(final int x, final int z, final int y, final int minY, final int maxY) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); + } + + @Override + public int getNearestSurfaceTerrainBlock( + final int x, + final int z, + final int y, + final int minY, + final int maxY, + final int failedMin, + final int failedMax + ) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); + } + + @Override + public int getNearestSurfaceTerrainBlock( + final int x, + final int z, + final int y, + final int minY, + final int maxY, + final int failedMin, + final int failedMax, + final boolean ignoreAir + ) { + return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir); + } + + @Override + public void addCaves(final Region region) throws WorldEditException { + extent.addCaves(region); + } + + @Override + public void generate(final Region region, final GenBase gen) throws WorldEditException { + extent.generate(region, gen); + } + + @Override + public void addSchems( + final Region region, + final Mask mask, + final List clipboards, + final int rarity, + final boolean rotate + ) throws WorldEditException { + extent.addSchems(region, mask, clipboards, rarity, rotate); + } + + @Override + public void spawnResource(final Region region, final Resource gen, final int rarity, final int frequency) throws + WorldEditException { + extent.spawnResource(region, gen, rarity, frequency); + } + + @Override + public boolean contains(final BlockVector3 pt) { + return extent.contains(pt); + } + + @Override + public boolean contains(final int x, final int y, final int z) { + return extent.contains(x, y, z); + } + + @Override + public void addOre( + final Region region, + final Mask mask, + final Pattern material, + final int size, + final int frequency, + final int rarity, + final int minY, + final int maxY + ) throws WorldEditException { + extent.addOre(region, mask, material, size, frequency, rarity, minY, maxY); + } + + @Override + public void addOres(final Region region, final Mask mask) throws WorldEditException { + extent.addOres(region, mask); + } + + @Override + public List> getBlockDistribution(final Region region) { + return extent.getBlockDistribution(region); + } + + @Override + public List> getBlockDistributionWithData(final Region region) { + return extent.getBlockDistributionWithData(region); + } + @Override public int getMaxY() { return extent.getMaxY(); } + @Override + public Clipboard lazyCopy(final Region region) { + return extent.lazyCopy(region); + } + + @Override + public int countBlocks(final Region region, final Set searchBlocks) { + return extent.countBlocks(region, searchBlocks); + } + + @Override + public int countBlocks(final Region region, final Mask searchMask) { + return extent.countBlocks(region, searchMask); + } + + @Override + public > int setBlocks(final Region region, final B block) throws MaxChangedBlocksException { + return extent.setBlocks(region, block); + } + + @Override + public int setBlocks(final Region region, final Pattern pattern) throws MaxChangedBlocksException { + return extent.setBlocks(region, pattern); + } + + @Override + public > int replaceBlocks( + final Region region, + final Set filter, + final B replacement + ) throws MaxChangedBlocksException { + return extent.replaceBlocks(region, filter, replacement); + } + + @Override + public int replaceBlocks(final Region region, final Set filter, final Pattern pattern) throws + MaxChangedBlocksException { + return extent.replaceBlocks(region, filter, pattern); + } + + @Override + public int replaceBlocks(final Region region, final Mask mask, final Pattern pattern) throws MaxChangedBlocksException { + return extent.replaceBlocks(region, mask, pattern); + } + + @Override + public int center(final Region region, final Pattern pattern) throws MaxChangedBlocksException { + return extent.center(region, pattern); + } + + @Override + public int setBlocks(final Set vset, final Pattern pattern) { + return extent.setBlocks(vset, pattern); + } + @Override public int getMinY() { return extent.getMinY(); @@ -295,6 +522,16 @@ public class AbstractDelegateExtent implements Extent { return this; } + @Override + public T apply(final Region region, final T filter, final boolean full) { + return extent.apply(region, filter, full); + } + + @Override + public T apply(final Iterable positions, final T filter) { + return extent.apply(positions, filter); + } + protected Operation commitBefore() { return null; } @@ -308,6 +545,11 @@ public class AbstractDelegateExtent implements Extent { public BiomeType getBiome(BlockVector3 position) { return extent.getBiome(position); } + + @Override + public int getEmittedLight(final BlockVector3 position) { + return extent.getEmittedLight(position); + } /* History */ @@ -317,16 +559,41 @@ public class AbstractDelegateExtent implements Extent { return extent.getEmittedLight(x, y, z); } + @Override + public int getSkyLight(final MutableBlockVector3 position) { + return extent.getSkyLight(position); + } + @Override public int getSkyLight(int x, int y, int z) { return extent.getSkyLight(x, y, z); } + @Override + public int getBrightness(final MutableBlockVector3 position) { + return extent.getBrightness(position); + } + @Override public int getBrightness(int x, int y, int z) { return extent.getBrightness(x, y, z); } + @Override + public int getOpacity(final MutableBlockVector3 position) { + return extent.getOpacity(position); + } + + @Override + public int getOpacity(final int x, final int y, final int z) { + return extent.getOpacity(x, y, z); + } + + @Override + public int[] getHeightMap(final HeightMapType type) { + return extent.getHeightMap(type); + } + public void setChangeSet(AbstractChangeSet changeSet) { if (extent instanceof HistoryExtent) { HistoryExtent history = ((HistoryExtent) extent); @@ -366,6 +633,12 @@ public class AbstractDelegateExtent implements Extent { return extent.fullySupports3DBiomes(); } + @SuppressWarnings("deprecation") + @Override + public boolean setBiome(final BlockVector2 position, final BiomeType biome) { + return extent.setBiome(position, biome); + } + @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { return extent.setBiome(x, y, z, biome); @@ -376,16 +649,31 @@ public class AbstractDelegateExtent implements Extent { return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome); } + @Override + public void setBlockLight(final BlockVector3 position, final int value) { + extent.setBlockLight(position, value); + } + @Override public void setBlockLight(int x, int y, int z, int value) { extent.setSkyLight(x, y, z, value); } + @Override + public void setSkyLight(final BlockVector3 position, final int value) { + extent.setSkyLight(position, value); + } + @Override public void setSkyLight(int x, int y, int z, int value) { extent.setSkyLight(x, y, z, value); } + @Override + public void setHeightMap(final HeightMapType type, final int[] heightMap) { + extent.setHeightMap(type, heightMap); + } + @Override public String toString() { return super.toString() + ":" + (extent == this ? "" : extent.toString()); diff --git a/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java b/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java new file mode 100644 index 000000000..1db8f6526 --- /dev/null +++ b/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java @@ -0,0 +1,30 @@ +package com.fastasyncworldedit; + +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ArchitecturalTest { + + public static Stream abstractDelegateExtentMethods() { + return Arrays.stream(AbstractDelegateExtent.class.getMethods()) + .filter(m -> m.getDeclaringClass() != Object.class) // ignore methods inherited from java.lang.Object + // TODO: figure out why enableHistory returns STQE instead of PQE when overriding + .filter(m -> !m.getName().equals("enableHistory")) + .map(Arguments::of); + } + + @ParameterizedTest + @MethodSource("abstractDelegateExtentMethods") + void testAbstractDelegateExtentOverridesAllMethods(Method method) { + assertEquals(AbstractDelegateExtent.class, method.getDeclaringClass()); + } + +} From dd6197922c199da9d1054b14b26c69015fbdb324 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 10 May 2023 17:27:44 +0100 Subject: [PATCH 13/65] fix: remove invalid file length check - NBT is stored at the end of the clipboard file - Fixes #2209 --- .../clipboard/DiskOptimizedClipboard.java | 27 ------------------- 1 file changed, 27 deletions(-) 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 100e3265f..b54c075fc 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 @@ -173,7 +173,6 @@ public class DiskOptimizedClipboard extends LinearClipboard { nbtMap = new HashMap<>(); try { this.file = file; - checkFileLength(file); this.braf = new RandomAccessFile(file, "rw"); braf.setLength(file.length()); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length(); @@ -202,32 +201,6 @@ public class DiskOptimizedClipboard extends LinearClipboard { } } - private void checkFileLength(File file) throws IOException { - long expectedFileSize = headerSize + ((long) getVolume() << 1); - if (file.length() > Integer.MAX_VALUE) { - if (expectedFileSize >= Integer.MAX_VALUE) { - throw new IOException(String.format( - "Cannot load clipboard of file size: %d > 2147483647 bytes (2.147 GiB), " + "volume: %d blocks", - file.length(), - getVolume() - )); - } else { - throw new IOException(String.format( - "Cannot load clipboard of file size > 2147483647 bytes (2.147 GiB). Possible corrupt file? Mismatch" + - " between volume `%d` and file length `%d`!", - file.length(), - getVolume() - )); - } - } else if (expectedFileSize != file.length()) { - throw new IOException(String.format( - "Possible corrupt clipboard file? Mismatch between expected file size `%d` and actual file size `%d`!", - expectedFileSize, - file.length() - )); - } - } - /** * Attempt to load a file into a new {@link DiskOptimizedClipboard} instance. Will attempt to recover on version mismatch * failure. From c57fee5b86aecc8dc7904b6777f24bb6507ed419 Mon Sep 17 00:00:00 2001 From: Phillipp Glanz Date: Fri, 12 May 2023 13:12:08 +0200 Subject: [PATCH 14/65] Fixes command context for bukkit console command sender (#2193) --- .../java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java | 6 +++++- .../worldedit/command/util/annotation/ConfirmHandler.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 876517dd6..f1edf65f7 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -112,11 +112,15 @@ public class WorldEditPlugin extends JavaPlugin { private BukkitServerInterface platform; private BukkitConfiguration config; private BukkitPermissionAttachmentManager permissionAttachmentManager; + // Fawe start + private BukkitCommandSender bukkitConsoleCommandSender; + // Fawe end @Override public void onLoad() { //FAWE start + this.bukkitConsoleCommandSender = new BukkitCommandSender(this, Bukkit.getConsoleSender()); // This is already covered by Spigot, however, a more pesky warning with a proper explanation over "Ambiguous plugin name..." can't hurt. Plugin[] plugins = Bukkit.getServer().getPluginManager().getPlugins(); for (Plugin p : plugins) { @@ -594,7 +598,7 @@ public class WorldEditPlugin extends JavaPlugin { return new BukkitBlockCommandSender(this, (BlockCommandSender) sender); } - return new BukkitCommandSender(this, sender); + return bukkitConsoleCommandSender; } public BukkitServerInterface getInternalPlatform() { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java index 5bf1ccc6c..067f9cfeb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/annotation/ConfirmHandler.java @@ -24,7 +24,7 @@ public class ConfirmHandler implements CommandCallListener { } Optional actorOpt = parameters.injectedValue(Key.of(Actor.class)); - if (!actorOpt.isPresent()) { + if (actorOpt.isEmpty()) { return; } Actor actor = actorOpt.get(); From 71bac009db7d470aeb2dcd37b3a4a856955d0958 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Fri, 12 May 2023 23:13:35 +0200 Subject: [PATCH 15/65] Properly scale the random in SimpleRandomCollection (#2220) --- .../core/util/collection/SimpleRandomCollection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java index d67227abe..dc2107559 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java @@ -36,7 +36,7 @@ public class SimpleRandomCollection extends RandomCollection { @Override public E next(int x, int y, int z) { - return map.ceilingEntry(getRandom().nextDouble(x, y, z)).getValue(); + return map.ceilingEntry(getRandom().nextDouble(x, y, z) * this.total).getValue(); } } From 319cb636e5721337ebfc8f84529cb15ab1a9c16f Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 12 May 2023 14:46:38 -0700 Subject: [PATCH 16/65] Simplify Java location in Jenkinsfile (#2221) --- Jenkinsfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ec2813783..8c6ece0eb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,14 +4,13 @@ pipeline { disableConcurrentBuilds() } stages { - stage('Set JDK 17') { - steps { - tool name: 'Temurin-17.0.6+10', type: 'jdk' - } - } stage('Build') { steps { - sh './gradlew clean build' + withEnv([ + "PATH+JAVA=${tool 'Temurin-17.0.6+10'}/bin" + ]) { + sh './gradlew clean build' + } } } stage('Archive artifacts') { From a14035d430c32d49be99b11748034ecdad13f91b Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Tue, 16 May 2023 10:32:03 +0200 Subject: [PATCH 17/65] Revert "Actually delegate in AbstractDelegateExtent" (#2229) --- .../extent/AbstractDelegateExtent.java | 296 +----------------- .../fastasyncworldedit/ArchitecturalTest.java | 30 -- 2 files changed, 4 insertions(+), 322 deletions(-) delete mode 100644 worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 06b10b62b..9dfd373ea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -23,43 +23,29 @@ import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.NullExtent; -import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; -import com.fastasyncworldedit.core.function.generator.GenBase; -import com.fastasyncworldedit.core.function.generator.Resource; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.math.MutableBlockVector3; -import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.OperationQueue; -import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.List; -import java.util.Set; import java.util.UUID; import static com.google.common.base.Preconditions.checkNotNull; @@ -67,7 +53,9 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * A base class for {@link Extent}s that merely passes extents onto another. */ -public abstract class AbstractDelegateExtent implements Extent { +//FAWE start - made none abstract +public class AbstractDelegateExtent implements Extent { +//FAWE end private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -80,7 +68,7 @@ public abstract class AbstractDelegateExtent implements Extent { * * @param extent the extent */ - protected AbstractDelegateExtent(Extent extent) { + public AbstractDelegateExtent(Extent extent) { checkNotNull(extent); this.extent = extent; } @@ -127,12 +115,6 @@ public abstract class AbstractDelegateExtent implements Extent { //FAWE end } - @SuppressWarnings("deprecation") - @Override - public BiomeType getBiome(final BlockVector2 position) { - return extent.getBiome(position); - } - @Override public BlockVector3 getMinimumPoint() { return extent.getMinimumPoint(); @@ -243,220 +225,11 @@ public abstract class AbstractDelegateExtent implements Extent { } } - @Override - public boolean isWorld() { - return extent.isWorld(); - } - - @Override - public boolean regenerateChunk( - final int x, - final int z, - @Nullable final BiomeType type, - @Nullable final Long seed - ) { - return extent.regenerateChunk(x, z, type, seed); - } - - @Override - public int getHighestTerrainBlock(final int x, final int z, final int minY, final int maxY) { - return extent.getHighestTerrainBlock(x, z, minY, maxY); - } - - @Override - public int getHighestTerrainBlock(final int x, final int z, final int minY, final int maxY, final Mask filter) { - return extent.getHighestTerrainBlock(x, z, minY, maxY, filter); - } - - @Override - public int getNearestSurfaceLayer(final int x, final int z, final int y, final int minY, final int maxY) { - return extent.getNearestSurfaceLayer(x, z, y, minY, maxY); - } - - @Override - public int getNearestSurfaceTerrainBlock( - final int x, - final int z, - final int y, - final int minY, - final int maxY, - final int failedMin, - final int failedMax, - final Mask mask - ) { - return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask); - } - - @Override - public int getNearestSurfaceTerrainBlock( - final int x, - final int z, - final int y, - final int minY, - final int maxY, - final boolean ignoreAir - ) { - return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir); - } - - @Override - public int getNearestSurfaceTerrainBlock(final int x, final int z, final int y, final int minY, final int maxY) { - return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY); - } - - @Override - public int getNearestSurfaceTerrainBlock( - final int x, - final int z, - final int y, - final int minY, - final int maxY, - final int failedMin, - final int failedMax - ) { - return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax); - } - - @Override - public int getNearestSurfaceTerrainBlock( - final int x, - final int z, - final int y, - final int minY, - final int maxY, - final int failedMin, - final int failedMax, - final boolean ignoreAir - ) { - return extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir); - } - - @Override - public void addCaves(final Region region) throws WorldEditException { - extent.addCaves(region); - } - - @Override - public void generate(final Region region, final GenBase gen) throws WorldEditException { - extent.generate(region, gen); - } - - @Override - public void addSchems( - final Region region, - final Mask mask, - final List clipboards, - final int rarity, - final boolean rotate - ) throws WorldEditException { - extent.addSchems(region, mask, clipboards, rarity, rotate); - } - - @Override - public void spawnResource(final Region region, final Resource gen, final int rarity, final int frequency) throws - WorldEditException { - extent.spawnResource(region, gen, rarity, frequency); - } - - @Override - public boolean contains(final BlockVector3 pt) { - return extent.contains(pt); - } - - @Override - public boolean contains(final int x, final int y, final int z) { - return extent.contains(x, y, z); - } - - @Override - public void addOre( - final Region region, - final Mask mask, - final Pattern material, - final int size, - final int frequency, - final int rarity, - final int minY, - final int maxY - ) throws WorldEditException { - extent.addOre(region, mask, material, size, frequency, rarity, minY, maxY); - } - - @Override - public void addOres(final Region region, final Mask mask) throws WorldEditException { - extent.addOres(region, mask); - } - - @Override - public List> getBlockDistribution(final Region region) { - return extent.getBlockDistribution(region); - } - - @Override - public List> getBlockDistributionWithData(final Region region) { - return extent.getBlockDistributionWithData(region); - } - @Override public int getMaxY() { return extent.getMaxY(); } - @Override - public Clipboard lazyCopy(final Region region) { - return extent.lazyCopy(region); - } - - @Override - public int countBlocks(final Region region, final Set searchBlocks) { - return extent.countBlocks(region, searchBlocks); - } - - @Override - public int countBlocks(final Region region, final Mask searchMask) { - return extent.countBlocks(region, searchMask); - } - - @Override - public > int setBlocks(final Region region, final B block) throws MaxChangedBlocksException { - return extent.setBlocks(region, block); - } - - @Override - public int setBlocks(final Region region, final Pattern pattern) throws MaxChangedBlocksException { - return extent.setBlocks(region, pattern); - } - - @Override - public > int replaceBlocks( - final Region region, - final Set filter, - final B replacement - ) throws MaxChangedBlocksException { - return extent.replaceBlocks(region, filter, replacement); - } - - @Override - public int replaceBlocks(final Region region, final Set filter, final Pattern pattern) throws - MaxChangedBlocksException { - return extent.replaceBlocks(region, filter, pattern); - } - - @Override - public int replaceBlocks(final Region region, final Mask mask, final Pattern pattern) throws MaxChangedBlocksException { - return extent.replaceBlocks(region, mask, pattern); - } - - @Override - public int center(final Region region, final Pattern pattern) throws MaxChangedBlocksException { - return extent.center(region, pattern); - } - - @Override - public int setBlocks(final Set vset, final Pattern pattern) { - return extent.setBlocks(vset, pattern); - } - @Override public int getMinY() { return extent.getMinY(); @@ -522,16 +295,6 @@ public abstract class AbstractDelegateExtent implements Extent { return this; } - @Override - public T apply(final Region region, final T filter, final boolean full) { - return extent.apply(region, filter, full); - } - - @Override - public T apply(final Iterable positions, final T filter) { - return extent.apply(positions, filter); - } - protected Operation commitBefore() { return null; } @@ -545,11 +308,6 @@ public abstract class AbstractDelegateExtent implements Extent { public BiomeType getBiome(BlockVector3 position) { return extent.getBiome(position); } - - @Override - public int getEmittedLight(final BlockVector3 position) { - return extent.getEmittedLight(position); - } /* History */ @@ -559,41 +317,16 @@ public abstract class AbstractDelegateExtent implements Extent { return extent.getEmittedLight(x, y, z); } - @Override - public int getSkyLight(final MutableBlockVector3 position) { - return extent.getSkyLight(position); - } - @Override public int getSkyLight(int x, int y, int z) { return extent.getSkyLight(x, y, z); } - @Override - public int getBrightness(final MutableBlockVector3 position) { - return extent.getBrightness(position); - } - @Override public int getBrightness(int x, int y, int z) { return extent.getBrightness(x, y, z); } - @Override - public int getOpacity(final MutableBlockVector3 position) { - return extent.getOpacity(position); - } - - @Override - public int getOpacity(final int x, final int y, final int z) { - return extent.getOpacity(x, y, z); - } - - @Override - public int[] getHeightMap(final HeightMapType type) { - return extent.getHeightMap(type); - } - public void setChangeSet(AbstractChangeSet changeSet) { if (extent instanceof HistoryExtent) { HistoryExtent history = ((HistoryExtent) extent); @@ -633,12 +366,6 @@ public abstract class AbstractDelegateExtent implements Extent { return extent.fullySupports3DBiomes(); } - @SuppressWarnings("deprecation") - @Override - public boolean setBiome(final BlockVector2 position, final BiomeType biome) { - return extent.setBiome(position, biome); - } - @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { return extent.setBiome(x, y, z, biome); @@ -649,31 +376,16 @@ public abstract class AbstractDelegateExtent implements Extent { return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome); } - @Override - public void setBlockLight(final BlockVector3 position, final int value) { - extent.setBlockLight(position, value); - } - @Override public void setBlockLight(int x, int y, int z, int value) { extent.setSkyLight(x, y, z, value); } - @Override - public void setSkyLight(final BlockVector3 position, final int value) { - extent.setSkyLight(position, value); - } - @Override public void setSkyLight(int x, int y, int z, int value) { extent.setSkyLight(x, y, z, value); } - @Override - public void setHeightMap(final HeightMapType type, final int[] heightMap) { - extent.setHeightMap(type, heightMap); - } - @Override public String toString() { return super.toString() + ":" + (extent == this ? "" : extent.toString()); diff --git a/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java b/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java deleted file mode 100644 index 1db8f6526..000000000 --- a/worldedit-core/src/test/java/com/fastasyncworldedit/ArchitecturalTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.fastasyncworldedit; - -import com.sk89q.worldedit.extent.AbstractDelegateExtent; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class ArchitecturalTest { - - public static Stream abstractDelegateExtentMethods() { - return Arrays.stream(AbstractDelegateExtent.class.getMethods()) - .filter(m -> m.getDeclaringClass() != Object.class) // ignore methods inherited from java.lang.Object - // TODO: figure out why enableHistory returns STQE instead of PQE when overriding - .filter(m -> !m.getName().equals("enableHistory")) - .map(Arguments::of); - } - - @ParameterizedTest - @MethodSource("abstractDelegateExtentMethods") - void testAbstractDelegateExtentOverridesAllMethods(Method method) { - assertEquals(AbstractDelegateExtent.class, method.getDeclaringClass()); - } - -} From 52723439dd76741e6a0e7a301f0420760615475d Mon Sep 17 00:00:00 2001 From: Marco Neuhaus Date: Sat, 20 May 2023 11:57:13 +0200 Subject: [PATCH 18/65] Fix reading of tile entities with "id" instead of "Id" tag (#2211) --- .../worldedit/extent/clipboard/io/SpongeSchematicReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index 9ad799b4c..464ef3e4f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -266,8 +266,8 @@ public class SpongeSchematicReader extends NBTSchematicReader { if (id == null) { continue; } + values.put("id", id); //FAWE end - values.put("id", values.get("Id")); values.remove("Id"); values.remove("Pos"); if (fixer != null) { From 2a08ad28a43a3e179c2b78178b27918ffd3c96a3 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 15:21:11 +0100 Subject: [PATCH 19/65] fix: correct issues with processor scopes (#2230) --- .../core/extent/DisallowedBlocksExtent.java | 6 +++--- .../fastasyncworldedit/core/extent/FaweRegionExtent.java | 2 +- .../core/extent/processor/MultiBatchProcessor.java | 2 +- .../core/extent/processor/ProcessorScope.java | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java index db270fa16..f1c523ce3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java @@ -141,7 +141,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB BlockState state = BlockTypesCache.states[block]; if (blockedBlocks != null) { if (blockedBlocks.contains(state.getBlockType().getId())) { - blocks[i] = 0; + blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; continue; } } @@ -150,7 +150,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB } for (FuzzyBlockState fuzzy : blockedStates) { if (fuzzy.equalsFuzzy(state)) { - blocks[i] = 0; + blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__; continue it; } } @@ -178,7 +178,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB @Override public ProcessorScope getScope() { - return ProcessorScope.CHANGING_BLOCKS; + return ProcessorScope.REMOVING_BLOCKS; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java index 6fa2767e0..84044f9b2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java @@ -168,7 +168,7 @@ public abstract class FaweRegionExtent extends ResettableExtent implements IBatc @Override public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; + return ProcessorScope.REMOVING_BLOCKS; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java index 85fe20986..e57ccb490 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java @@ -257,7 +257,7 @@ public class MultiBatchProcessor implements IBatchProcessor { for (IBatchProcessor processor : processors) { scope = Math.max(scope, processor.getScope().intValue()); } - return ProcessorScope.valueOf(0); + return ProcessorScope.valueOf(scope); } /** diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java index 7fdd3e67a..e3f09e0a3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/ProcessorScope.java @@ -3,9 +3,9 @@ package com.fastasyncworldedit.core.extent.processor; /** * The scope of a processor. * Order in which processors are executed: - * - ADDING_BLOCKS (processors that strictly ADD blocks to an edit ONLY) - * - CHANGING_BLOCKS (processors that strictly ADD or CHANGE blocks being set) - * - REMOVING_BLOCKS (processors that string ADD, CHANGE or REMOVE blocks being set) + * - ADDING_BLOCKS (processors that may ADD blocks to an edit ONLY) + * - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set) + * - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set) * - CUSTOM (processors that do not specify a SCOPE) * - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors) */ From e9b781d1273c6de119df87a9d33775df8e4086d5 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 15:22:02 +0100 Subject: [PATCH 20/65] fix: correctly in initialise unlimited limit (#2231) --- .../java/com/fastasyncworldedit/core/limit/FaweLimit.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java index c899c668d..01d18db31 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java @@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.limit; import com.fastasyncworldedit.core.FaweCache; +import java.util.Collections; import java.util.Set; public class FaweLimit { @@ -114,10 +115,10 @@ public class FaweLimit { MAX.FAST_PLACEMENT = true; MAX.CONFIRM_LARGE = true; MAX.RESTRICT_HISTORY_TO_REGIONS = false; - MAX.STRIP_NBT = null; + MAX.STRIP_NBT = Collections.emptySet(); MAX.UNIVERSAL_DISALLOWED_BLOCKS = false; - MAX.DISALLOWED_BLOCKS = null; - MAX.REMAP_PROPERTIES = null; + MAX.DISALLOWED_BLOCKS = Collections.emptySet(); + MAX.REMAP_PROPERTIES = Collections.emptySet(); } public boolean MAX_CHANGES() { From 3a13c4aaa7155acf54cc57a1b0ad724670b3dc92 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 15:25:24 +0100 Subject: [PATCH 21/65] chore: remove firework-chunk-loading prevention and default tick limiter to false (#2234) - It's very resource-intensive and probably doesn't work anyway - Tick limiter should not be enabled by default, it confuses a lot of people with the console logs --- .../bukkit/listener/ChunkListener.java | 42 ------------------- .../core/configuration/Settings.java | 8 +--- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java index 5d2e35335..7b9599084 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java @@ -371,48 +371,6 @@ public abstract class ChunkListener implements Listener { } } - /** - * Prevent firework from loading chunks. - */ - @EventHandler(priority = EventPriority.LOWEST) - public void onChunkLoad(ChunkLoadEvent event) { - if (!Settings.settings().TICK_LIMITER.FIREWORKS_LOAD_CHUNKS) { - Chunk chunk = event.getChunk(); - Entity[] entities = chunk.getEntities(); - World world = chunk.getWorld(); - - Exception e = new Exception(); - int start = 14; - int end = 22; - int depth = Math.min(end, getDepth(e)); - - for (int frame = start; frame < depth; frame++) { - StackTraceElement elem = getElement(e, frame); - if (elem == null) { - return; - } - String className = elem.getClassName(); - int len = className.length(); - if (len > 15 && className.charAt(len - 15) == 'E' && className - .endsWith("EntityFireworks")) { - for (Entity ent : world.getEntities()) { - if (ent.getType() == EntityType.FIREWORK) { - Vector velocity = ent.getVelocity(); - double vertical = Math.abs(velocity.getY()); - if (Math.abs(velocity.getX()) > vertical - || Math.abs(velocity.getZ()) > vertical) { - LOGGER.warn( - "[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at {}", - ent.getLocation()); - ent.remove(); - } - } - } - } - } - } - } - @EventHandler(priority = EventPriority.LOWEST) public void onItemSpawn(ItemSpawnEvent event) { if (physicsFreeze) { 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 502e9dbf3..44e211461 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 @@ -695,7 +695,7 @@ public class Settings extends Config { public static class TICK_LIMITER { @Comment("Enable the limiter") - public boolean ENABLED = true; + public boolean ENABLED = false; @Comment("The interval in ticks") public int INTERVAL = 20; @Comment("Max falling blocks per interval (per chunk)") @@ -704,12 +704,6 @@ public class Settings extends Config { public int PHYSICS_MS = 10; @Comment("Max item spawns per interval (per chunk)") public int ITEMS = 256; - @Comment({ - "Whether fireworks can load chunks", - " - Fireworks usually travel vertically so do not load any chunks", - " - Horizontal fireworks can be hacked in to crash a server" - }) - public boolean FIREWORKS_LOAD_CHUNKS = false; } From 1b0fb9ed482a5e72f1248b57d35861c4c48232a5 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 19:32:38 +0100 Subject: [PATCH 22/65] fix: more intuitive limit permissions (#2233) - immediately return limit if unlimited (limit combination take the "higher" value) - add fawe.limit.unlimited permission and it's an intuitive permission to give --- .../fastasyncworldedit/core/configuration/Settings.java | 7 +++---- 1 file changed, 3 insertions(+), 4 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 44e211461..50b515257 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 @@ -102,11 +102,10 @@ public class Settings extends Config { public FaweLimit getLimit(Actor actor) { FaweLimit limit; - if (actor.hasPermission("fawe.limit.*") || actor.hasPermission("fawe.bypass")) { - limit = FaweLimit.MAX.copy(); - } else { - limit = new FaweLimit(); + if (actor.hasPermission("fawe.bypass") || actor.hasPermission("fawe.limit.unlimited")) { + return FaweLimit.MAX.copy(); } + limit = new FaweLimit(); ArrayList keys = new ArrayList<>(LIMITS.getSections()); if (keys.remove("default")) { keys.add("default"); From 85f5006a0b9efb2034c3df5d44a129bb81fda857 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 19:33:18 +0100 Subject: [PATCH 23/65] fix: correctly handle creation of extents in EditSessionBuilder (#2232) - Add regionExtent process to the queue (fixes #2227) - Correctly wrap extents in each other --- .../sk89q/worldedit/EditSessionBuilder.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index c235ca784..012bac992 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -531,16 +531,14 @@ public final class EditSessionBuilder { } if (allowedRegions == null && Settings.settings().REGION_RESTRICTIONS) { if (actor != null && !actor.hasPermission("fawe.bypass.regions")) { - if (actor instanceof Player) { - Player player = (Player) actor; + if (actor instanceof Player player) { allowedRegions = player.getAllowedRegions(); } } } if (disallowedRegions == null && Settings.settings().REGION_RESTRICTIONS && Settings.settings().REGION_RESTRICTIONS_OPTIONS.ALLOW_BLACKLISTS) { if (actor != null && !actor.hasPermission("fawe.bypass.regions")) { - if (actor instanceof Player) { - Player player = (Player) actor; + if (actor instanceof Player player) { disallowedRegions = player.getDisallowedRegions(); } } @@ -561,6 +559,9 @@ public final class EditSessionBuilder { } } } + if (placeChunks && regionExtent != null) { + queue.addProcessor(regionExtent); + } // There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks if (placeChunks) { if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) { @@ -597,10 +598,9 @@ public final class EditSessionBuilder { this.extent = regionExtent; } if (this.limit != null && this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) { + this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); if (placeChunks) { - queue.addProcessor(new StripNBTExtent(this.extent, this.limit.STRIP_NBT)); - } else { - this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); + queue.addProcessor((IBatchProcessor) this.extent); } } if (this.limit != null && !this.limit.isUnlimited()) { @@ -613,10 +613,9 @@ public final class EditSessionBuilder { } Set> remaps = this.limit.REMAP_PROPERTIES; if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) { + this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps); if (placeChunks) { - queue.addProcessor(new DisallowedBlocksExtent(this.extent, limitBlocks, remaps)); - } else { - this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps); + queue.addProcessor((IBatchProcessor) this.extent); } } } From 264185a323d40c8ee5944977486f1bace20e1e33 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 20 May 2023 20:52:03 +0200 Subject: [PATCH 24/65] Add method to check initialize state in PlatformManager, deny access to registries when uninitialized --- .../extension/platform/PlatformManager.java | 14 ++++++++++++++ .../worldedit/registry/NamespacedRegistry.java | 10 +++++++++- .../com/sk89q/worldedit/registry/Registry.java | 13 +++++++++++++ .../com/sk89q/worldedit/world/biome/BiomeType.java | 2 +- .../sk89q/worldedit/world/block/BlockCategory.java | 2 +- .../com/sk89q/worldedit/world/block/BlockType.java | 2 +- .../sk89q/worldedit/world/entity/EntityType.java | 2 +- .../com/sk89q/worldedit/world/item/ItemType.java | 2 +- .../internal/expression/BaseExpressionTest.java | 2 ++ .../worldedit/util/collection/BlockMapTest.java | 7 +++++++ 10 files changed, 50 insertions(+), 6 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 406964c24..9f798e21c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -321,10 +321,24 @@ public class PlatformManager { return queryCapability(Capability.CONFIGURATION).getConfiguration(); } + /** + * Get the current supported {@link SideEffect}s. + * + * @return the supported side effects + * @throws NoCapablePlatformException thrown if no platform is capable + */ public Collection getSupportedSideEffects() { return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects(); } + /** + * Get the initialized state of the Platform. + * {@return if the platform manager is initialized} + */ + public boolean isInitialized() { + return initialized.get(); + } + /** * You shouldn't have been calling this anyways, but this is now deprecated. Either don't * fire this event at all, or fire the new event via the event bus if you're a platform. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java index 5fca65799..e6715907c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/NamespacedRegistry.java @@ -47,8 +47,16 @@ public final class NamespacedRegistry extends Registry { this(name, MINECRAFT_NAMESPACE); } + public NamespacedRegistry(final String name, final boolean checkInitialized) { + this(name, MINECRAFT_NAMESPACE, checkInitialized); + } + public NamespacedRegistry(final String name, final String defaultNamespace) { - super(name); + this(name, defaultNamespace, false); + } + + public NamespacedRegistry(final String name, final String defaultNamespace, final boolean checkInitialized) { + super(name, checkInitialized); this.defaultNamespace = defaultNamespace; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java index 2bb20010a..b29af49e8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.registry; +import com.sk89q.worldedit.WorldEdit; + import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; @@ -35,9 +37,15 @@ public class Registry implements Iterable { private final Map map = new HashMap<>(); private final String name; + private final boolean checkInitialized; public Registry(final String name) { + this(name, false); + } + + public Registry(final String name, final boolean checkInitialized) { this.name = name; + this.checkInitialized = checkInitialized; } public String getName() { @@ -53,6 +61,11 @@ public class Registry implements Iterable { @Nullable public V get(final String key) { checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase: %s", key); + if (this.checkInitialized) { + checkState( + WorldEdit.getInstance().getPlatformManager().isInitialized(), + "WorldEdit is not initialized yet."); + } return this.map.get(key); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java index ab503e1fb..e8501da69 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java @@ -32,7 +32,7 @@ import com.sk89q.worldedit.registry.NamespacedRegistry; public class BiomeType implements RegistryItem, Keyed, BiomePattern { //FAWE end - public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("biome type"); + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("biome type", true); private final String id; private int legacyId = -1; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java index 3ed6f7a3f..29d8ee53b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategory.java @@ -36,7 +36,7 @@ public class BlockCategory extends Category implements Keyed { //FAWE start private boolean[] flatMap; //FAWE end - public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block tag"); + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block tag", true); public BlockCategory(final String id) { super(id); 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 043a89bf5..01a5d5c8a 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 @@ -56,7 +56,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class BlockType implements Keyed, Pattern { //FAWE end - public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block type"); + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("block type", true); private static final Logger LOGGER = LogManagerCompat.getLogger(); private final String id; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java index 98a70b7e5..5a530c8a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityType.java @@ -27,7 +27,7 @@ import com.sk89q.worldedit.registry.NamespacedRegistry; public class EntityType implements RegistryItem, Keyed { //FAWE end - public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("entity type"); + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("entity type", true); private final String id; 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 97549320d..ecba92f8b 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 @@ -39,7 +39,7 @@ import javax.annotation.Nullable; public class ItemType implements RegistryItem, Keyed { //FAWE end - public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("item type"); + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("item type", true); private final String id; @SuppressWarnings("deprecation") diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java index 0fbf9556a..3491347a4 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -63,6 +64,7 @@ class BaseExpressionTest { }); WorldEdit.getInstance().getPlatformManager().register(mockPlat); WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent()); + assertTrue(WorldEdit.getInstance().getPlatformManager().isInitialized(), "Platform is not initialized"); WorldEdit.getInstance().getConfiguration().calculationTimeout = 1_000; } diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java index 1bb09ee17..595993919 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java @@ -87,10 +87,17 @@ class BlockMapTest { Stream.of(Capability.values()) .collect(Collectors.toMap(Function.identity(), __ -> Preference.NORMAL)) ); + when(MOCKED_PLATFORM.getConfiguration()).thenReturn(new LocalConfiguration() { + @Override + public void load() { + } + }); PlatformManager platformManager = WorldEdit.getInstance().getPlatformManager(); platformManager.register(MOCKED_PLATFORM); WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent()); + assertTrue(WorldEdit.getInstance().getPlatformManager().isInitialized(), "Platform is not initialized"); + registerBlock("minecraft:air"); registerBlock("minecraft:oak_wood"); } From 85214c944f83028d7c3ab6becdc4981ef0fb1194 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 20 May 2023 20:54:52 +0200 Subject: [PATCH 25/65] Keep extending cuboid selector after //paste -s/n --- .../com/sk89q/worldedit/command/ClipboardCommands.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 0cb9f6798..2e7b53440 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 @@ -74,6 +74,7 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionIntersection; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -546,7 +547,12 @@ public class ClipboardCommands { Vector3 max = realTo.add(holder .getTransform() .apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3())); - RegionSelector selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + final CuboidRegionSelector selector; + if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { + selector = new ExtendingCuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + } else { + selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + } session.setRegionSelector(world, selector); selector.learnChanges(); selector.explainRegionAdjust(actor, session); From 3cac28ea8415bd6b9cb00de4b73fcc2a5fd728cf Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 20 May 2023 20:56:37 +0200 Subject: [PATCH 26/65] Keep extending cuboid selector after //cli selectworld --- .../java/com/sk89q/worldedit/cli/CLIExtraCommands.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIExtraCommands.java b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIExtraCommands.java index 9f6389463..27cc08821 100644 --- a/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIExtraCommands.java +++ b/worldedit-cli/src/main/java/com/sk89q/worldedit/cli/CLIExtraCommands.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.task.Task; import com.sk89q.worldedit.world.World; @@ -39,9 +40,12 @@ public class CLIExtraCommands { desc = "Select the entire world" ) public void selectWorld(Actor actor, World world, LocalSession session) { - session.setRegionSelector(world, new CuboidRegionSelector( - world, world.getMinimumPoint(), world.getMaximumPoint() - )); + final CuboidRegionSelector selector; + if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { + selector = new ExtendingCuboidRegionSelector(world, world.getMinimumPoint(), world.getMaximumPoint()); + } else { + selector = new CuboidRegionSelector(world, world.getMinimumPoint(), world.getMaximumPoint()); + } actor.printInfo(TextComponent.of("Selected the entire world.")); } From 2c7b529f67ddaae565930217e9a94330cb1dcf16 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 20 May 2023 19:56:46 +0100 Subject: [PATCH 27/65] fix: create single threaded executor for use with non-concurrent generators (#2236) - Fixes #2228 --- .../com/fastasyncworldedit/bukkit/adapter/Regenerator.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index c95fbc580..7c00ebb2b 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -165,7 +165,11 @@ public abstract class Regenerator Date: Sat, 20 May 2023 20:58:47 +0200 Subject: [PATCH 28/65] fix(core): Properly reinit convex CUI selection on primary click --- .../regions/selector/ConvexPolyhedralRegionSelector.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java index ced04eddf..70471ceae 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/ConvexPolyhedralRegionSelector.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.cui.CUIRegion; import com.sk89q.worldedit.internal.cui.SelectionPointEvent; import com.sk89q.worldedit.internal.cui.SelectionPolygonEvent; +import com.sk89q.worldedit.internal.cui.SelectionShapeEvent; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; @@ -206,6 +207,7 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion checkNotNull(session); checkNotNull(pos); + session.dispatchCUIEvent(player, new SelectionShapeEvent(getTypeID())); session.describeCUI(player); player.print(Caption.of("worldedit.selection.convex.explain.primary", TextComponent.of(pos.toString()))); @@ -226,6 +228,7 @@ public class ConvexPolyhedralRegionSelector implements RegionSelector, CUIRegion public void explainRegionAdjust(Actor player, LocalSession session) { checkNotNull(player); checkNotNull(session); + session.dispatchCUIEvent(player, new SelectionShapeEvent(getTypeID())); session.describeCUI(player); } From 5e3222c55dff0cc66c309030fa2f7fa95e18a59e Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 20 May 2023 21:00:35 +0200 Subject: [PATCH 29/65] Ensure blockmap uniqueness across TE values --- .../util/collection/Int2BaseBlockMap.java | 2 +- .../util/collection/BlockMapTest.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java index bf8e3bed5..fca089a68 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java @@ -170,7 +170,7 @@ class Int2BaseBlockMap extends AbstractInt2ObjectMap { return old; } int oldId = commonMap.put(key, internalId); - return assumeAsBlock(oldId); + return BlockStateIdAccess.isValidInternalId(oldId) ? assumeAsBlock(oldId) : uncommonMap.remove(key); } @Override diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java index 595993919..9558ac0bb 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit.util.collection; import com.google.common.collect.ImmutableMap; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.event.platform.PlatformsRegisteredEvent; import com.sk89q.worldedit.extension.platform.Capability; @@ -100,6 +102,7 @@ class BlockMapTest { registerBlock("minecraft:air"); registerBlock("minecraft:oak_wood"); + registerBlock("minecraft:chest"); } @AfterAll @@ -123,6 +126,7 @@ class BlockMapTest { private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock(); private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock(); + private final BaseBlock chestWithNbt = checkNotNull(BlockTypes.CHEST).getDefaultState().toBaseBlock(new CompoundTag(ImmutableMap.of("dummy", new StringTag("value")))); private AutoCloseable mocks; @@ -748,6 +752,22 @@ class BlockMapTest { }); } + @SuppressWarnings("OverwrittenKey") + @Test + @DisplayName("put with valid and invalid keys doesn't duplicate") + void putWithInvalidAndValid() { + generator.makeVectorsStream().forEach(vec -> { + BlockMap map = BlockMap.createForBaseBlock(); + // This tests https://github.com/EngineHub/WorldEdit/issues/2250 + // Due to two internal maps, a bug existed where both could have the same value + map.put(vec, chestWithNbt); + map.put(vec, air); + + assertEquals(1, map.size()); + assertEquals(air, map.get(vec)); + }); + } + } @Test From 86f06b75271cbfce381d5e121dc80f5020c14f7c Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 20 May 2023 21:01:34 +0200 Subject: [PATCH 30/65] Add -Penginehub.obf.none=true gradle property to build mojmap worldedit-bukkit. --- worldedit-bukkit/build.gradle.kts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index a6c5147ad..461d36c99 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -47,7 +47,13 @@ val adapters = configurations.create("adapters") { isCanBeResolved = true shouldResolveConsistentlyWith(configurations["runtimeClasspath"]) attributes { - attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED)) + attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, + if ((project.findProperty("enginehub.obf.none") as String?).toBoolean()) { + objects.named(Obfuscation.NONE) + } else { + objects.named(Obfuscation.OBFUSCATED) + } + ) } } From 4c0b535e6e177ccdfde5932e2ca5f87c141ea963 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 21 May 2023 09:50:10 +0200 Subject: [PATCH 31/65] Ensure non-Double types are boxed in expression function calls. --- .../java/com/sk89q/worldedit/internal/expression/Functions.java | 1 + 1 file changed, 1 insertion(+) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java index 369694f0b..6f3a63787 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Functions.java @@ -75,6 +75,7 @@ public final class Functions { ); handle = handle.asType(handle.type().changeReturnType(Number.class)); handle = filterReturnValue(handle, DOUBLE_VALUE); + handle = handle.asType(handle.type().wrap()); } // return vararg-ity if (wasVarargs) { From 2bdc925b0f1d2eb60de7598f4c092610a5f0c464 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 21 May 2023 09:51:59 +0200 Subject: [PATCH 32/65] Add regression tests to the round Expression function --- .../worldedit/internal/expression/ExpressionTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java index ec175bd72..a1d418f85 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/ExpressionTest.java @@ -449,4 +449,14 @@ class ExpressionTest extends BaseExpressionTest { assertTrue(e.getMessage().contains("Calculations exceeded time limit")); } + @Test + public void testRound() { + checkTestCase("round(1.3)", 1); + checkTestCase("round(0.9)", 1); + checkTestCase("round(-1.1)", -1); + checkTestCase("round(-0.9)", -1); + checkTestCase("round(1.5)", 2); + checkTestCase("round(-1.5)", -1); + } + } From 74aff920a8c00618ec47a1fe0070cce5a15180b6 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 22 May 2023 19:32:36 +0100 Subject: [PATCH 33/65] fix: fix universal disallowed blocks logic in limit and EditSession compilation (#2237) - Fixes #2184 --- .../main/java/com/fastasyncworldedit/core/limit/FaweLimit.java | 2 +- .../src/main/java/com/sk89q/worldedit/EditSessionBuilder.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java index 01d18db31..00a47178c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java @@ -242,7 +242,7 @@ public class FaweLimit { && FAST_PLACEMENT && !RESTRICT_HISTORY_TO_REGIONS && (STRIP_NBT == null || STRIP_NBT.isEmpty()) - && !UNIVERSAL_DISALLOWED_BLOCKS + // && !UNIVERSAL_DISALLOWED_BLOCKS --> do not include this, it effectively has no relevance && (DISALLOWED_BLOCKS == null || DISALLOWED_BLOCKS.isEmpty()) && (REMAP_PROPERTIES == null || REMAP_PROPERTIES.isEmpty()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index 012bac992..2512af96c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -605,7 +605,7 @@ public final class EditSessionBuilder { } if (this.limit != null && !this.limit.isUnlimited()) { Set limitBlocks = new HashSet<>(); - if ((getActor() == null || getActor().hasPermission("worldedit.anyblock")) && this.limit.UNIVERSAL_DISALLOWED_BLOCKS) { + if (getActor() != null && !getActor().hasPermission("worldedit.anyblock") && this.limit.UNIVERSAL_DISALLOWED_BLOCKS) { limitBlocks.addAll(WorldEdit.getInstance().getConfiguration().disallowedBlocks); } if (this.limit.DISALLOWED_BLOCKS != null && !this.limit.DISALLOWED_BLOCKS.isEmpty()) { From 7c01c1e95e9e8b35e23ad852f8260a08a3a02e8b Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 22 May 2023 19:32:56 +0100 Subject: [PATCH 34/65] feat: allow captions in SuggestInputParseException (#2239) - Deprecate for removal methods using string message - Fixes #2026 --- .../command/SuggestInputParseException.java | 72 ++++++++++++++++--- .../factory/parser/mask/RichMaskParser.java | 4 +- .../parser/pattern/RichPatternParser.java | 4 +- .../core/function/mask/BlockMaskBuilder.java | 7 +- .../worldedit/world/block/BlockState.java | 10 +-- .../worldedit/world/block/BlockTypes.java | 4 +- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java index c38b38888..bd10e6d20 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java @@ -1,6 +1,8 @@ package com.fastasyncworldedit.core.command; +import com.fastasyncworldedit.core.configuration.Caption; import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.util.formatting.text.Component; import java.lang.reflect.InvocationTargetException; import java.util.List; @@ -13,33 +15,81 @@ public class SuggestInputParseException extends InputParseException { private final InputParseException cause; private final Supplier> getSuggestions; - private String prefix; + /** + * @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(Component, Supplier)} + */ + @Deprecated(forRemoval = true, since = "TODO") public SuggestInputParseException(String msg, String prefix, Supplier> getSuggestions) { - this(new InputParseException(msg), prefix, getSuggestions); + this(new InputParseException(msg), getSuggestions); } + /** + * @deprecated Use {@link SuggestInputParseException#of(Throwable, Supplier)} + */ + @Deprecated(forRemoval = true, since = "TODO") public static SuggestInputParseException of(Throwable other, String prefix, Supplier> getSuggestions) { if (other instanceof InputParseException) { - return of((InputParseException) other, prefix, getSuggestions); + return of((InputParseException) other, getSuggestions); } - return of(new InputParseException(other.getMessage()), prefix, getSuggestions); + return of(new InputParseException(other.getMessage()), getSuggestions); } + /** + * @deprecated Use {@link SuggestInputParseException#of(InputParseException, Supplier)} + */ + @Deprecated(forRemoval = true, since = "TODO") public static SuggestInputParseException of(InputParseException other, String prefix, Supplier> getSuggestions) { if (other instanceof SuggestInputParseException) { return (SuggestInputParseException) other; } - return new SuggestInputParseException(other, prefix, getSuggestions); + return new SuggestInputParseException(other, getSuggestions); } + /** + * @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(InputParseException, Supplier)} + */ + @Deprecated(forRemoval = true, since = "TODO") public SuggestInputParseException(InputParseException other, String prefix, Supplier> getSuggestions) { - super(other.getMessage()); + super(other.getRichMessage()); + checkNotNull(getSuggestions); + checkNotNull(other); + this.cause = other; + this.getSuggestions = getSuggestions; + } + + /** + * Create a new SuggestInputParseException instance + * + * @param message Message to send + * @param getSuggestions Supplier of list of sugegstions to give to user + * @since TODO + */ + public SuggestInputParseException(Component message, Supplier> getSuggestions) { + this(new InputParseException(message), getSuggestions); + } + + public static SuggestInputParseException of(Throwable other, Supplier> getSuggestions) { + if (other instanceof InputParseException) { + return of((InputParseException) other, getSuggestions); + } + //noinspection deprecation + return of(new InputParseException(other.getMessage()), getSuggestions); + } + + public static SuggestInputParseException of(InputParseException other, Supplier> getSuggestions) { + if (other instanceof SuggestInputParseException) { + return (SuggestInputParseException) other; + } + return new SuggestInputParseException(other, getSuggestions); + } + + public SuggestInputParseException(InputParseException other, Supplier> getSuggestions) { + super(other.getRichMessage()); checkNotNull(getSuggestions); checkNotNull(other); this.cause = other; this.getSuggestions = getSuggestions; - this.prefix = prefix; } public static SuggestInputParseException get(InvocationTargetException e) { @@ -54,7 +104,7 @@ public class SuggestInputParseException extends InputParseException { } public static SuggestInputParseException of(String input, List values) { - throw new SuggestInputParseException("No value: " + input, input, () -> + throw new SuggestInputParseException(Caption.of("fawe.error.no-value-for-input", input), () -> values.stream() .map(Object::toString) .filter(v -> v.startsWith(input)) @@ -76,8 +126,12 @@ public class SuggestInputParseException extends InputParseException { return getSuggestions.get(); } + /** + * @deprecated Unused + */ + @Deprecated(forRemoval = true, since = "TODO") public SuggestInputParseException prepend(String input) { - this.prefix = input + prefix; + // Do nothing return this; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java index 31c8f3d3e..f86645b67 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java @@ -50,7 +50,7 @@ public class RichMaskParser extends FaweParser { @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (input.isEmpty()) { - throw new SuggestInputParseException("No input provided", "", () -> Stream + throw new SuggestInputParseException(Caption.of("fawe.error.no-input-provided"), () -> Stream .of("#", ",", "&") .map(n -> n + ":") .collect(Collectors.toList()) @@ -95,7 +95,6 @@ public class RichMaskParser extends FaweParser { "https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns" )) )), - full, () -> { if (full.length() == 1) { return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("")); @@ -162,7 +161,6 @@ public class RichMaskParser extends FaweParser { "https://intellectualsites.github.io/fastasyncworldedit-documentation/masks/masks" )) )), - full, () -> { if (full.length() == 1) { return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("")); 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 4971ee878..3ebd84fa6 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 @@ -47,8 +47,7 @@ public class RichPatternParser extends FaweParser { public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { if (input.isEmpty()) { throw new SuggestInputParseException( - "No input provided", - "", + Caption.of("fawe.error.no-input-provided"), () -> Stream .concat(Stream.of("#", ",", "&"), BlockTypes.getNameSpaces().stream().map(n -> n + ":")) .collect(Collectors.toList()) @@ -88,7 +87,6 @@ public class RichPatternParser extends FaweParser { "https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns" )) )), - full, () -> { if (full.length() == 1) { return new ArrayList<>(worldEdit.getPatternFactory().getSuggestions("")); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java index 0b89e0ada..5bfeb766c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java @@ -178,8 +178,7 @@ public class BlockMaskBuilder { } if (operator == null) { throw new SuggestInputParseException( - "No operator for " + input, - "", + Caption.of("fawe.error.no-operator-for-input", input), () -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=") ); } @@ -195,7 +194,7 @@ public class BlockMaskBuilder { String value = charSequence.toString(); final PropertyKey fKey = key; Collection types = type != null ? Collections.singleton(type) : blockTypeList; - throw new SuggestInputParseException("No value for " + input, input, () -> { + throw new SuggestInputParseException(Caption.of("fawe.error.no-value-for-input", input), () -> { HashSet values = new HashSet<>(); types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> { Property p = t.getProperty(fKey); @@ -287,7 +286,7 @@ public class BlockMaskBuilder { } private void suggest(String input, String property, Collection finalTypes) throws InputParseException { - throw new SuggestInputParseException(input + " does not have: " + property, input, () -> { + throw new SuggestInputParseException(Caption.of("worldedit.error.parser.unknown-property", property, input), () -> { Set keys = PropertyKeySet.empty(); finalTypes.forEach(t -> t.getProperties().forEach(p -> keys.add(p.getKey()))); return keys.stream().map(PropertyKey::getName) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index 87959ebe6..f08675fc5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.world.block; import com.fastasyncworldedit.core.command.SuggestInputParseException; +import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.function.mask.SingleBlockStateMask; import com.fastasyncworldedit.core.queue.ITileInput; import com.fastasyncworldedit.core.registry.state.PropertyKey; @@ -43,6 +44,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.AbstractProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.world.registry.BlockMaterial; @@ -150,7 +152,7 @@ public class BlockState implements BlockStateHolder, Pattern { type = BlockTypes.get(key); if (type == null) { String input = key.toString(); - throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of( + throw new SuggestInputParseException(Caption.of("fawe.error.invalid-block-type", TextComponent.of(input)), () -> Stream.of( BlockTypesCache.values) .map(BlockType::getId) .filter(id -> StringMan.blockStateMatches(input, id)) @@ -211,8 +213,7 @@ public class BlockState implements BlockStateHolder, Pattern { String input = charSequence.toString(); BlockType finalType = type; throw new SuggestInputParseException( - "Invalid property " + key + ":" + input + " for type " + type, - input, + Caption.of("worldedit.error.parser.unknown-property", key + ":" + input, type), () -> finalType.getProperties().stream() .map(Property::getName) @@ -222,8 +223,7 @@ public class BlockState implements BlockStateHolder, Pattern { ); } else { throw new SuggestInputParseException( - "No operator for " + state, - "", + Caption.of("fawe.error.no-operator-for-input", state), () -> Collections.singletonList("=") ); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index e79a9af5a..b8af4bce5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -20,9 +20,11 @@ package com.sk89q.worldedit.world.block; import com.fastasyncworldedit.core.command.SuggestInputParseException; +import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.util.StringMan; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.registry.LegacyMapper; import javax.annotation.Nullable; @@ -1967,7 +1969,7 @@ public final class BlockTypes { } } - throw new SuggestInputParseException("Does not match a valid block type: " + inputLower, inputLower, () -> Stream.of( + throw new SuggestInputParseException(Caption.of("fawe.error.invalid-block-type", TextComponent.of(input)), () -> Stream.of( BlockTypesCache.values) .filter(b -> StringMan.blockStateMatches(inputLower, b.getId())) .map(BlockType::getId) From 435161d566e8d80341d90cb19a4d5fbf23276e8c Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 22 May 2023 19:33:15 +0100 Subject: [PATCH 35/65] fix: only send invalid region messages with debug true (#2238) - Addresses #2188 --- .../main/java/com/fastasyncworldedit/core/util/WEManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/WEManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/WEManager.java index 85f4eb2d1..439b486e3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/WEManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/WEManager.java @@ -130,7 +130,9 @@ public class WEManager { backupRegions.add(region); } } else { - player.print(Caption.of("fawe.error.region-mask-invalid", mask.getClass().getSimpleName())); + if (Settings.settings().ENABLED_COMPONENTS.DEBUG) { + player.printDebug(Caption.of("fawe.error.region-mask-invalid", mask.getClass().getSimpleName())); + } removed = true; iterator.remove(); } From 5dca925bc30774f1205197c034a2b7cbd7bcb8c1 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 22 May 2023 19:33:59 +0100 Subject: [PATCH 36/65] feat: make more commands unqueued (#2235) * feat: make more commands unqueued - Effectively fixes the very specific "issue" in #2223 I guess - Use this opportunity to clean up the command classes a bit * Address comments --- .../worldedit/command/BiomeCommands.java | 5 +- .../worldedit/command/BrushCommands.java | 85 ++++++++++--------- .../worldedit/command/ChunkCommands.java | 10 ++- .../worldedit/command/ClipboardCommands.java | 64 ++++++-------- .../worldedit/command/GeneralCommands.java | 27 +++--- .../worldedit/command/GenerationCommands.java | 48 +++++------ .../worldedit/command/HistorySubCommands.java | 24 ++++-- .../worldedit/command/NavigationCommands.java | 25 ++++-- .../worldedit/command/RegionCommands.java | 37 +++++--- .../worldedit/command/SchematicCommands.java | 25 +++--- .../worldedit/command/SelectionCommands.java | 16 +++- .../worldedit/command/SnapshotCommands.java | 8 +- .../command/SnapshotUtilCommands.java | 2 +- .../worldedit/command/ToolUtilCommands.java | 4 +- .../worldedit/command/UtilityCommands.java | 54 ++++++------ 15 files changed, 233 insertions(+), 201 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 149af4eed..0b2bd576d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -82,7 +82,10 @@ public class BiomeCommands { aliases = {"biomels", "/biomelist", "/listbiomes"}, desc = "Gets all biomes available." ) - @CommandPermissions("worldedit.biome.list") + @CommandPermissions( + value = "worldedit.biome.list", + queued = false + ) public void biomeList( Actor actor, @ArgFlag(name = 'p', desc = "Page number.", def = "1") 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 5c87d9136..2149951e0 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 @@ -310,11 +310,11 @@ public class BrushCommands { }, desc = "Join multiple objects together in a curve", descFooter = - "Click to select some objects,click the same block twice to connect the objects.\n" - + "Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops.\n" - + "Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png\n" - + "Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.png" - + "Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/" + """ + Click to select some objects,click the same block twice to connect the objects. + Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops. + Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png + Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.pngTutorial: https://www.planetminecraft.com/blog/fawe-tutorial/""" ) @CommandPermissions("worldedit.brush.spline") public void splineBrush( @@ -337,9 +337,10 @@ public class BrushCommands { "vaesweep" }, desc = "Sweep your clipboard content along a curve", - descFooter = "Sweeps your clipboard content along a curve.\n" - + "Define a curve by selecting the individual points with a brush\n" - + "Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve" + descFooter = """ + Sweeps your clipboard content along a curve. + Define a curve by selecting the individual points with a brush + Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve""" ) @CommandPermissions("worldedit.brush.sweep") public void sweepBrush( @@ -636,10 +637,10 @@ public class BrushCommands { @Command( name = "layer", desc = "Replaces terrain with a layer.", - descFooter = "Replaces terrain with a layer.\n" - + "Example: /br layer 5 oak_planks,orange_stained_glass,magenta_stained_glass,black_wool - Places several " + - "layers on a surface\n" - + "Pic: https://i.imgur.com/XV0vYoX.png" + descFooter = """ + Replaces terrain with a layer. + Example: /br layer 5 oak_planks,orange_stained_glass,magenta_stained_glass,black_wool - Places several layers on a surface + Pic: https://i.imgur.com/XV0vYoX.png""" ) @CommandPermissions("worldedit.brush.layer") public void surfaceLayer( @@ -658,11 +659,11 @@ public class BrushCommands { @Command( name = "splatter", desc = "Splatter a pattern on a surface", - descFooter = "Sets a bunch of blocks randomly on a surface.\n" - + "Pic: https://i.imgur.com/hMD29oO.png\n" - + "Example: /br splatter stone,dirt 30 15\n" - + "Note: The seeds define how many splotches there are, recursion defines how large, " - + "solid defines whether the pattern is applied per seed, else per block." + descFooter = """ + Sets a bunch of blocks randomly on a surface. + Pic: https://i.imgur.com/hMD29oO.png + Example: /br splatter stone,dirt 30 15 + Note: The seeds define how many splotches there are, recursion defines how large, solid defines whether the pattern is applied per seed, else per block.""" ) @CommandPermissions("worldedit.brush.splatter") public void splatterBrush( @@ -691,9 +692,10 @@ public class BrushCommands { "scommand" }, desc = "Run commands at random points on a surface", - descFooter = "Run commands at random points on a surface\n" - + " - Your selection will be expanded to the specified size around each point\n" - + " - Placeholders: {x}, {y}, {z}, {world}, {size}" + descFooter = """ + Run commands at random points on a surface + - Your selection will be expanded to the specified size around each point + - Placeholders: {x}, {y}, {z}, {world}, {size}""" ) @CommandPermissions("worldedit.brush.scattercommand") public void scatterCommandBrush( @@ -723,9 +725,10 @@ public class BrushCommands { name = "height", aliases = {"heightmap"}, desc = "Raise or lower terrain using a heightmap", - descFooter = "This brush raises and lowers land.\n" - + "Note: Use a negative yscale to reduce height\n" - + "Snow Pic: https://i.imgur.com/Hrzn0I4.png" + descFooter = """ + This brush raises and lowers land. + Note: Use a negative yscale to reduce height + Snow Pic: https://i.imgur.com/Hrzn0I4.png""" ) @CommandPermissions("worldedit.brush.height") public void heightBrush( @@ -890,9 +893,11 @@ public class BrushCommands { "copypasta" }, desc = "Copy Paste brush", - descFooter = "Left click the base of an object to copy.\n" + "Right click to paste\n" - + "Note: Works well with the clipboard scroll action\n" - + "Video: https://www.youtube.com/watch?v=RPZIaTbqoZw" + descFooter = """ + Left click the base of an object to copy. + Right click to paste + Note: Works well with the clipboard scroll action + Video: https://www.youtube.com/watch?v=RPZIaTbqoZw""" ) @CommandPermissions("worldedit.brush.copy") public void copy( @@ -914,9 +919,10 @@ public class BrushCommands { name = "command", aliases = {"cmd"}, desc = "Command brush", - descFooter = "Run the commands at the clicked position.\n" - + " - Your selection will be expanded to the specified size around each point\n" - + " - Placeholders: {x}, {y}, {z}, {world}, {size}" + descFooter = """ + Run the commands at the clicked position. + - Your selection will be expanded to the specified size around each point + - Placeholders: {x}, {y}, {z}, {world}, {size}""" ) @CommandPermissions("worldedit.brush.command") public void command( @@ -1036,7 +1042,7 @@ public class BrushCommands { ) throws WorldEditException { WorldEdit.getInstance().checkMaxBrushRadius(radius); - BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); + BrushTool tool = session.getBrushTool(player); tool.setSize(radius); tool.setFill(null); tool.setBrush(new OperationFactoryBrush(factory, shape, session), permission); @@ -1196,17 +1202,12 @@ public class BrushCommands { brush = new HollowSphereBrush(); } else { //FAWE start - Suggest different brush material if sand or gravel is used - if (pattern instanceof BlockStateHolder) { - BlockType type = ((BlockStateHolder) pattern).getBlockType(); - switch (type.getId()) { - case "minecraft:sand": - case "minecraft:gravel": - player.print( - Caption.of("fawe.worldedit.brush.brush.try.other")); - falling = true; - break; - default: - break; + if (pattern instanceof BlockStateHolder holder) { + BlockType type = holder.getBlockType(); + if (type == BlockTypes.SAND || type == BlockTypes.GRAVEL) { + player.print( + Caption.of("fawe.worldedit.brush.brush.try.other")); + falling = true; } } if (falling) { @@ -1361,7 +1362,7 @@ public class BrushCommands { iterations = Math.min(limit.MAX_ITERATIONS, iterations); //FAWE end - set(context, new SnowSmoothBrush(iterations, mask), "worldedit.brush.snowsmooth").setSize(radius); + set(context, new SnowSmoothBrush(iterations, snowBlockCount, mask), "worldedit.brush.snowsmooth").setSize(radius); player.print(Caption.of( "worldedit.brush.smooth.equip", radius, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java index d1b9084fb..c36252f85 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java @@ -79,7 +79,10 @@ public class ChunkCommands { aliases = {"/chunkinfo"}, desc = "Get information about the chunk you're inside" ) - @CommandPermissions("worldedit.chunkinfo") + @CommandPermissions( + value = "worldedit.chunkinfo", + queued = false + ) public void chunkInfo(Player player) { Location pos = player.getBlockLocation(); int chunkX = (int) Math.floor(pos.getBlockX() / 16.0); @@ -99,7 +102,10 @@ public class ChunkCommands { aliases = {"/listchunks"}, desc = "List chunks that your selection includes" ) - @CommandPermissions("worldedit.listchunks") + @CommandPermissions( + value = "worldedit.listchunks", + queued = false + ) public void listChunks( Actor actor, World world, LocalSession session, @ArgFlag(name = 'p', desc = "Page number.", def = "1") int page 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 2e7b53440..40ca530c8 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 @@ -160,35 +160,7 @@ public class ClipboardCommands { session.getPlacementPosition(actor)); ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint()); copy.setCopyingEntities(copyEntities); - copy.setCopyingBiomes(copyBiomes); - - Mask sourceMask = editSession.getSourceMask(); - Region[] regions = editSession.getAllowedRegions(); - Region allowedRegion; - if (regions == null || regions.length == 0) { - allowedRegion = new NullRegion(); - } else { - allowedRegion = new RegionIntersection(regions); - } - final Mask firstSourceMask = mask != null ? mask : sourceMask; - final Mask finalMask = MaskIntersection.of(firstSourceMask, new RegionMask(allowedRegion)).optimize(); - if (finalMask != Masks.alwaysTrue()) { - copy.setSourceMask(finalMask); - } - if (sourceMask != null) { - editSession.setSourceMask(null); - new MaskTraverser(sourceMask).reset(editSession); - editSession.setSourceMask(null); - } - - try { - Operations.completeLegacy(copy); - } catch (Throwable e) { - throw e; - } finally { - clipboard.flush(); - } - session.setClipboard(new ClipboardHolder(clipboard)); + createCopy(session, editSession, copyBiomes, mask, clipboard, copy); copy.getStatusMessages().forEach(actor::print); //FAWE end @@ -299,7 +271,25 @@ public class ClipboardCommands { copy.setSourceFunction(new BlockReplace(editSession, leavePattern)); copy.setCopyingEntities(copyEntities); copy.setRemovingEntities(true); + createCopy(session, editSession, copyBiomes, mask, clipboard, copy); + + if (!actor.hasPermission("fawe.tips")) { + actor.print(Caption.of("fawe.tips.tip.lazycut")); + } + copy.getStatusMessages().forEach(actor::print); + //FAWE end + } + + private void createCopy( + final LocalSession session, + final EditSession editSession, + final boolean copyBiomes, + final Mask mask, + final Clipboard clipboard, + final ForwardExtentCopy copy + ) { copy.setCopyingBiomes(copyBiomes); + Mask sourceMask = editSession.getSourceMask(); Region[] regions = editSession.getAllowedRegions(); Region allowedRegion; @@ -318,20 +308,13 @@ public class ClipboardCommands { new MaskTraverser(sourceMask).reset(editSession); editSession.setSourceMask(null); } + try { Operations.completeLegacy(copy); - } catch (Throwable e) { - throw e; } finally { clipboard.flush(); } session.setClipboard(new ClipboardHolder(clipboard)); - - if (!actor.hasPermission("fawe.tips")) { - actor.print(Caption.of("fawe.tips.tip.lazycut")); - } - copy.getStatusMessages().forEach(actor::print); - //FAWE end } //FAWE start @@ -583,9 +566,10 @@ public class ClipboardCommands { @Command( name = "/rotate", desc = "Rotate the contents of the clipboard", - descFooter = "Non-destructively rotate the contents of the clipboard.\n" - + "Angles are provided in degrees and a positive angle will result in a clockwise rotation. " - + "Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.\n" + descFooter = """ + Non-destructively rotate the contents of the clipboard. + Angles are provided in degrees and a positive angle will result in a clockwise rotation. Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees. + """ ) @CommandPermissions("worldedit.clipboard.rotate") public void rotate( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 3d54e40a7..0cf5b1a78 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -149,14 +149,11 @@ public class GeneralCommands { String arg0 = args.get(0).toLowerCase(Locale.ENGLISH); String flipped; switch (arg0) { - case "on": - flipped = "off"; - break; - case "off": - flipped = "on"; - break; - default: + case "on" -> flipped = "off"; + case "off" -> flipped = "on"; + default -> { return TextComponent.of("There is no replacement for //fast " + arg0); + } } return CommandUtil.createNewCommandReplacementText("//perf " + flipped); } @@ -362,7 +359,10 @@ public class GeneralCommands { descFooter = "This is dependent on platform implementation. " + "Not all platforms support watchdog hooks, or contain a watchdog." ) - @CommandPermissions("worldedit.watchdog") + @CommandPermissions( + value = "worldedit.watchdog", + queued = false + ) public void watchdog( Actor actor, LocalSession session, @Arg(desc = "The mode to set the watchdog hook to", def = "") @@ -424,7 +424,10 @@ public class GeneralCommands { aliases = {"/searchitem", "/l", "/search"}, desc = "Search for an item" ) - @CommandPermissions("worldedit.searchitem") + @CommandPermissions( + value = "worldedit.searchitem", + queued = false + ) public void searchItem( Actor actor, @Switch(name = 'b', desc = "Only search for blocks") @@ -573,7 +576,10 @@ public class GeneralCommands { aliases = {"tips"}, desc = "Toggle FAWE tips" ) - @CommandPermissions("fawe.tips") + @CommandPermissions( + value = "fawe.tips", + queued = false + ) public void tips(Actor actor, LocalSession session) throws WorldEditException { if (actor.togglePermission("fawe.tips")) { actor.print(Caption.of("fawe.info.worldedit.toggle.tips.on")); @@ -627,7 +633,6 @@ public class GeneralCommands { String command = "/searchitem " + (blocksOnly ? "-b " : "") + (itemsOnly ? "-i " : "") + "-p %page% " + search; Map results = new TreeMap<>(); String idMatch = search.replace(' ', '_'); - String nameMatch = search.toLowerCase(Locale.ROOT); for (ItemType searchType : ItemType.REGISTRY) { if (blocksOnly && !searchType.hasBlockType()) { continue; 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 dabc1dc92..a1e6034f5 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 @@ -119,18 +119,15 @@ public class GenerationCommands { final double radiusX; final double radiusZ; switch (radii.size()) { - case 1: - radiusX = radiusZ = Math.max(1, radii.get(0)); - break; - - case 2: + 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)); - break; - - default: + } + default -> { actor.print(Caption.of("worldedit.cyl.invalid-radius")); return 0; + } } worldEdit.checkMaxRadius(radiusX); worldEdit.checkMaxRadius(radiusZ); @@ -169,18 +166,15 @@ public class GenerationCommands { final double radiusX; final double radiusZ; switch (radii.size()) { - case 1: - radiusX = radiusZ = Math.max(1, radii.get(0)); - break; - - case 2: + 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)); - break; - - default: + } + default -> { actor.print(Caption.of("worldedit.cyl.invalid-radius")); return 0; + } } worldEdit.checkMaxRadius(radiusX); @@ -234,19 +228,16 @@ public class GenerationCommands { final double radiusY; final double radiusZ; switch (radii.size()) { - case 1: - radiusX = radiusY = radiusZ = Math.max(0, radii.get(0)); - break; - - case 3: + case 1 -> radiusX = radiusY = radiusZ = Math.max(0, radii.get(0)); + case 3 -> { radiusX = Math.max(0, radii.get(0)); radiusY = Math.max(0, radii.get(1)); radiusZ = Math.max(0, radii.get(2)); - break; - - default: + } + default -> { actor.print(Caption.of("worldedit.sphere.invalid-radius")); return 0; + } } worldEdit.checkMaxRadius(radiusX); @@ -437,9 +428,10 @@ public class GenerationCommands { name = "/generatebiome", aliases = {"/genbiome", "/gb"}, desc = "Sets biome according to a formula.", - descFooter = "Formula must return positive numbers (true) if the point is inside the shape\n" - + "Sets the biome of blocks in that shape.\n" - + "For details, see https://ehub.to/we/expr" + descFooter = """ + Formula must return positive numbers (true) if the point is inside the shape + Sets the biome of blocks in that shape. + For details, see https://ehub.to/we/expr""" ) @CommandPermissions("worldedit.generation.shape.biome") @Logging(ALL) @@ -626,14 +618,12 @@ public class GenerationCommands { BlockVector3 pos1 = session.getPlacementPosition(actor); BlockVector3 pos2 = pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1); CuboidRegion region = new CuboidRegion(pos1, pos2); - int[] count = new int[1]; final BufferedImage finalImage = image; RegionVisitor visitor = new RegionVisitor(region, pos -> { int x = pos.getBlockX() - pos1.getBlockX(); int z = pos.getBlockZ() - pos1.getBlockZ(); int color = finalImage.getRGB(x, z); BlockType block = tu.getNearestBlock(color); - count[0]++; if (block != null) { return editSession.setBlock(pos, block.getDefaultState()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java index 3688041d4..452d5084c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java @@ -223,7 +223,10 @@ public class HistorySubCommands { aliases = {"summary", "summarize"}, desc = "Summarize an edit" ) - @CommandPermissions("worldedit.history.info") + @CommandPermissions( + value = "worldedit.history.info", + queued = false + ) public synchronized void summary( Player player, RollbackDatabase database, Arguments arguments, @Arg(desc = "Player uuid/name") @@ -314,8 +317,7 @@ public class HistorySubCommands { public Component apply(@Nullable Supplier input) { ChangeSet edit = input.get(); - if (edit instanceof RollbackOptimizedHistory) { - RollbackOptimizedHistory rollback = (RollbackOptimizedHistory) edit; + if (edit instanceof RollbackOptimizedHistory rollback) { UUID uuid = rollback.getUUID(); int index = rollback.getIndex(); @@ -368,7 +370,10 @@ public class HistorySubCommands { aliases = {"inspect", "search", "near"}, desc = "Find nearby edits" ) - @CommandPermissions("worldedit.history.find") + @CommandPermissions( + value = "worldedit.history.find", + queued = false + ) public synchronized void find( Player player, World world, RollbackDatabase database, Arguments arguments, @ArgFlag(name = 'u', def = "", desc = "String user") @@ -429,7 +434,10 @@ public class HistorySubCommands { aliases = {"distribution"}, desc = "View block distribution for an edit" ) - @CommandPermissions("worldedit.history.distr") + @CommandPermissions( + value = "worldedit.history.distr", + queued = false + ) public void distr( Player player, LocalSession session, RollbackDatabase database, Arguments arguments, @Arg(desc = "Player uuid/name") @@ -468,7 +476,10 @@ public class HistorySubCommands { name = "list", desc = "List your history" ) - @CommandPermissions("worldedit.history.list") + @CommandPermissions( + value = "worldedit.history.list", + queued = false + ) public void list( Player player, LocalSession session, RollbackDatabase database, Arguments arguments, @Arg(desc = "Player uuid/name") @@ -476,7 +487,6 @@ public class HistorySubCommands { @ArgFlag(name = 'p', desc = "Page to view.", def = "") Integer page ) { - int index = session.getHistoryIndex(); List> history = Lists.transform( session.getHistory(), (Function>) input -> () -> input diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java index 22e8a12ec..239105cd2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java @@ -60,7 +60,10 @@ public class NavigationCommands { aliases = {"!", "/unstuck"}, desc = "Escape from being stuck inside a block" ) - @CommandPermissions("worldedit.navigation.unstuck") + @CommandPermissions( + value = "worldedit.navigation.unstuck", + queued = false + ) public void unstuck(Player player) throws WorldEditException { player.findFreePosition(); player.print(Caption.of("worldedit.unstuck.moved")); @@ -71,7 +74,10 @@ public class NavigationCommands { aliases = {"asc", "/asc", "/ascend"}, desc = "Go up a floor" ) - @CommandPermissions("worldedit.navigation.ascend") + @CommandPermissions( + value = "worldedit.navigation.ascend", + queued = false + ) public void ascend( Player player, @Arg(desc = "# of levels to ascend", def = "1") @@ -96,7 +102,10 @@ public class NavigationCommands { aliases = {"desc", "/desc", "/descend"}, desc = "Go down a floor" ) - @CommandPermissions("worldedit.navigation.descend") + @CommandPermissions( + value = "worldedit.navigation.descend", + queued = false + ) public void descend( Player player, @Arg(desc = "# of levels to descend", def = "1") @@ -147,7 +156,10 @@ public class NavigationCommands { aliases = {"/thru"}, desc = "Pass through walls" ) - @CommandPermissions("worldedit.navigation.thru.command") + @CommandPermissions( + value = "worldedit.navigation.thru.command", + queued = false + ) public void thru(Player player) throws WorldEditException { if (player.passThroughForwardWall(6)) { player.print(Caption.of("worldedit.thru.moved")); @@ -161,7 +173,10 @@ public class NavigationCommands { aliases = {"j", "/jumpto", "/j"}, desc = "Teleport to a location" ) - @CommandPermissions("worldedit.navigation.jumpto.command") + @CommandPermissions( + value = "worldedit.navigation.jumpto.command", + queued = false + ) public void jumpTo( Player player, @Arg(desc = "Location to jump to", def = "") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 5d3fb30e8..a72cba4b3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -140,7 +140,10 @@ public class RegionCommands { name = "/test", desc = "test region" ) - @CommandPermissions("worldedit.region.test") + @CommandPermissions( + value = "worldedit.region.test", + queued = false + ) @Logging(REGION) public void test( Actor actor, EditSession editSession, @@ -175,7 +178,10 @@ public class RegionCommands { aliases = "/nbt", desc = "View nbt info for a block" ) - @CommandPermissions("worldedit.nbtinfo") + @CommandPermissions( + value = "worldedit.nbtinfo", + queued = false + ) public void nbtinfo(Player player, EditSession editSession) { Location pos = player.getBlockTrace(128); if (pos == null) { @@ -228,13 +234,12 @@ public class RegionCommands { @Switch(name = 'h', desc = "Generate only a shell") boolean shell ) throws WorldEditException { - if (!(region instanceof CuboidRegion)) { + if (!(region instanceof CuboidRegion cuboidregion)) { actor.print(Caption.of("worldedit.line.cuboid-only")); return 0; } checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); - CuboidRegion cuboidregion = (CuboidRegion) region; BlockVector3 pos1 = cuboidregion.getPos1(); BlockVector3 pos2 = cuboidregion.getPos2(); int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell); @@ -261,13 +266,12 @@ public class RegionCommands { @Switch(name = 'h', desc = "Generate only a shell") boolean shell ) throws WorldEditException { - if (!(region instanceof ConvexPolyhedralRegion)) { + if (!(region instanceof ConvexPolyhedralRegion cpregion)) { actor.print(Caption.of("worldedit.curve.invalid-type")); return 0; } checkCommandArgument(thickness >= 0, "Thickness must be >= 0"); - ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; List vectors = new ArrayList<>(cpregion.getVertices()); int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell); @@ -468,7 +472,10 @@ public class RegionCommands { desc = "Bypass region restrictions", descFooter = "Bypass region restrictions" ) - @CommandPermissions("fawe.admin") + @CommandPermissions( + value = "fawe.admin", + queued = false + ) public void wea(Actor actor) throws WorldEditException { if (actor.togglePermission("fawe.bypass")) { actor.print(Caption.of("fawe.info.worldedit.bypassed")); @@ -697,7 +704,7 @@ public class RegionCommands { actor.print(Caption.of("fawe.regen.time")); //FAWE end RegenOptions options = RegenOptions.builder() - .seed(!randomSeed ? seed : new Long(ThreadLocalRandom.current().nextLong())) + .seed(!randomSeed ? seed : Long.valueOf(ThreadLocalRandom.current().nextLong())) .regenBiomes(regenBiomes) .biomeType(biomeType) .build(); @@ -718,9 +725,10 @@ public class RegionCommands { @Command( name = "/deform", desc = "Deforms a selected region with an expression", - descFooter = "The expression is executed for each block and is expected\n" - + "to modify the variables x, y and z to point to a new block\n" - + "to fetch. For details, see https://ehub.to/we/expr" + descFooter = """ + The expression is executed for each block and is expected + to modify the variables x, y and z to point to a new block + to fetch. For details, see https://ehub.to/we/expr""" ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) @@ -794,9 +802,10 @@ public class RegionCommands { @Command( name = "/hollow", desc = "Hollows out the object contained in this selection", - descFooter = "Hollows out the object contained in this selection.\n" - + "Optionally fills the hollowed out part with the given block.\n" - + "Thickness is measured in manhattan distance." + descFooter = """ + Hollows out the object contained in this selection. + Optionally fills the hollowed out part with the given block. + Thickness is measured in manhattan distance.""" ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 03d19e72c..20dee10c3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -26,7 +26,6 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure; import com.fastasyncworldedit.core.util.MainUtil; -import com.google.common.base.Function; import com.google.common.collect.Multimap; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; @@ -90,6 +89,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.function.Function; import java.util.regex.Pattern; import static com.fastasyncworldedit.core.util.ReflectionUtils.as; @@ -209,11 +209,9 @@ public class SchematicCommands { } ClipboardHolder clipboard = session.getClipboard(); - if (clipboard instanceof URIClipboardHolder) { - URIClipboardHolder identifiable = (URIClipboardHolder) clipboard; + if (clipboard instanceof URIClipboardHolder identifiable) { if (identifiable.contains(uri)) { - if (identifiable instanceof MultiClipboardHolder) { - MultiClipboardHolder multi = (MultiClipboardHolder) identifiable; + if (identifiable instanceof MultiClipboardHolder multi) { multi.remove(uri); if (multi.getHolders().isEmpty()) { session.setClipboard(null); @@ -319,7 +317,7 @@ public class SchematicCommands { LocalConfiguration config = worldEdit.getConfiguration(); //FAWE start - ClipboardFormat format = null; + ClipboardFormat format; InputStream in = null; try { URI uri; @@ -526,7 +524,10 @@ public class SchematicCommands { aliases = {"listformats", "f"}, desc = "List available formats" ) - @CommandPermissions("worldedit.schematic.formats") + @CommandPermissions( + value = "worldedit.schematic.formats", + queued = false + ) public void formats(Actor actor) { actor.print(Caption.of("worldedit.schematic.formats.title")); StringBuilder builder; @@ -552,7 +553,10 @@ public class SchematicCommands { desc = "List saved schematics", descFooter = "Note: Format is not fully verified until loading." ) - @CommandPermissions("worldedit.schematic.list") + @CommandPermissions( + value = "worldedit.schematic.list", + queued = false + ) public void list( Actor actor, LocalSession session, @ArgFlag(name = 'p', desc = "Page to view.", def = "1") @@ -823,7 +827,6 @@ public class SchematicCommands { final String SCHEMATIC_NAME = file.getName(); double oldKbOverwritten = 0; - String overwrittenPath = curFilepath; int numFiles = -1; if (checkFilesize) { @@ -839,10 +842,10 @@ public class SchematicCommands { if (overwrite) { oldKbOverwritten = Files.size(Paths.get(file.getAbsolutePath())) / 1000.0; int iter = 1; - while (new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension()).exists()) { + while (new File(curFilepath + "." + iter + "." + format.getPrimaryFileExtension()).exists()) { iter++; } - file = new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension()); + file = new File(curFilepath + "." + iter + "." + format.getPrimaryFileExtension()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index e6e06e9a2..64e3c8a81 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -314,7 +314,10 @@ public class SelectionCommands { name = "/wand", desc = "Get the wand object" ) - @CommandPermissions("worldedit.wand") + @CommandPermissions( + value = "worldedit.wand", + queued = false + ) public void wand( Player player, LocalSession session, @Switch(name = 'n', desc = "Get a navigation wand") boolean navWand @@ -348,7 +351,10 @@ public class SelectionCommands { aliases = {"/toggleeditwand"}, desc = "Remind the user that the wand is now a tool and can be unbound with /tool none." ) - @CommandPermissions("worldedit.wand.toggle") + @CommandPermissions( + value = "worldedit.wand.toggle", + queued = false + ) public void toggleWand(Player player) { player.print( Caption.of( @@ -499,7 +505,10 @@ public class SelectionCommands { name = "/size", desc = "Get information about the selection" ) - @CommandPermissions("worldedit.selection.size") + @CommandPermissions( + value = "worldedit.selection.size", + queued = false + ) public void size( Actor actor, World world, LocalSession session, @Switch(name = 'c', desc = "Get clipboard info instead") @@ -734,6 +743,7 @@ public class SelectionCommands { box.appendCommand("sphere", Caption.of("worldedit.select.sphere.description"), "//sel sphere"); box.appendCommand("cyl", Caption.of("worldedit.select.cyl.description"), "//sel cyl"); box.appendCommand("convex", Caption.of("worldedit.select.convex.description"), "//sel convex"); + //FAWE start box.appendCommand("polyhedral", Caption.of("fawe.selection.sel.polyhedral"), "//sel polyhedral"); box.appendCommand("fuzzy[=]", Caption.of("fawe.selection.sel.fuzzy-instruction"), "//sel fuzzy[=]"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java index 66cfdd853..6d9bda084 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java @@ -98,7 +98,10 @@ public class SnapshotCommands { name = "list", desc = "List snapshots" ) - @CommandPermissions("worldedit.snapshots.list") + @CommandPermissions( + value = "worldedit.snapshots.list", + queued = false + ) void list( Actor actor, World world, @ArgFlag(name = 'p', desc = "Page of results to return", def = "1") @@ -127,8 +130,7 @@ public class SnapshotCommands { TextComponent.of(world.getName()) )); - if (config.snapshotDatabase instanceof FileSystemSnapshotDatabase) { - FileSystemSnapshotDatabase db = (FileSystemSnapshotDatabase) config.snapshotDatabase; + if (config.snapshotDatabase instanceof FileSystemSnapshotDatabase db) { Path root = db.getRoot(); if (Files.isDirectory(root)) { WorldEdit.logger.info("No snapshots were found for world '" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java index 8de52f6aa..b98785631 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java @@ -95,7 +95,7 @@ public class SnapshotUtilCommands { if (snapshotName != null) { URI uri = resolveSnapshotName(config, snapshotName); Optional snapOpt = config.snapshotDatabase.getSnapshot(uri); - if (!snapOpt.isPresent()) { + if (snapOpt.isEmpty()) { actor.print(Caption.of("worldedit.restore.not-available")); return; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index 6f47132f7..0694b2af3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -140,7 +140,7 @@ public class ToolUtilCommands { @Arg(desc = "The range of the brush") int range ) throws WorldEditException { - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setRange(range); + session.getBrushTool(player).setRange(range); player.print(Caption.of("worldedit.tool.range.set")); } @@ -156,7 +156,7 @@ public class ToolUtilCommands { ) throws WorldEditException { we.checkMaxBrushRadius(size); - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size); + session.getBrushTool(player).setSize(size); player.print(Caption.of("worldedit.tool.size.set")); } 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 b3403bba4..f064868b4 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 @@ -29,7 +29,6 @@ import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.image.ImageUtil; import com.fastasyncworldedit.core.util.task.DelegateConsumer; -import com.google.common.base.Function; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalConfiguration; @@ -98,6 +97,7 @@ import java.util.Locale; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -130,7 +130,10 @@ public class UtilityCommands { aliases = {"/hmi", "hmi"}, desc = "Generate the heightmap interface: https://github.com/IntellectualSites/HeightMap" ) - @CommandPermissions("fawe.admin") + @CommandPermissions( + value = "fawe.admin", + queued = false + ) public void heightmapInterface( Actor actor, @Arg(name = "min", desc = "int", def = "100") int min, @@ -145,12 +148,9 @@ public class UtilityCommands { final int sub = srcFolder.getAbsolutePath().length(); List images = new ArrayList<>(); MainUtil.iterateFiles(srcFolder, file -> { - switch (file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase(Locale.ROOT)) { - case ".png": - case ".jpeg": - break; - default: - return; + String s = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase(Locale.ROOT); + if (!s.equals(".png") && !s.equals(".jpeg")) { + return; } try { String name = file.getAbsolutePath().substring(sub); @@ -187,7 +187,7 @@ public class UtilityCommands { StringBuilder config = new StringBuilder(); config.append("var images = [\n"); for (String image : images) { - config.append('"' + image.replace(File.separator, "/") + "\",\n"); + config.append('"').append(image.replace(File.separator, "/")).append("\",\n"); } config.append("];\n"); config.append("// The low res images (they should all be the same size)\n"); @@ -805,7 +805,10 @@ public class UtilityCommands { name = "/help", desc = "Displays help for WorldEdit commands" ) - @CommandPermissions("worldedit.help") + @CommandPermissions( + value = "worldedit.help", + queued = false + ) public void help( Actor actor, @Switch(name = 's', desc = "List sub-commands of the given command, if applicable") @@ -859,7 +862,6 @@ public class UtilityCommands { URI uri = input.getKey(); String path = input.getValue(); - boolean url = false; boolean loaded = isLoaded.apply(uri); URIType type = URIType.FILE; @@ -959,21 +961,13 @@ public class UtilityCommands { if (len > 0) { for (String arg : args) { switch (arg.toLowerCase(Locale.ROOT)) { - case "me": - case "mine": - case "local": - case "private": - listMine = true; - break; - case "public": - case "global": - listGlobal = true; - break; - case "all": + case "me", "mine", "local", "private" -> listMine = true; + case "public", "global" -> listGlobal = true; + case "all" -> { listMine = true; listGlobal = true; - break; - default: + } + default -> { if (arg.endsWith("/") || arg.endsWith(File.separator)) { arg = arg.replace("/", File.separator); String newDirFilter = dirFilter + arg; @@ -995,7 +989,7 @@ public class UtilityCommands { } else { filters.add(arg); } - break; + } } } } @@ -1005,7 +999,7 @@ public class UtilityCommands { List toFilter = new ArrayList<>(); if (!filters.isEmpty()) { - forEachFile = new DelegateConsumer(forEachFile) { + forEachFile = new DelegateConsumer<>(forEachFile) { @Override public void accept(File file) { toFilter.add(file); @@ -1015,7 +1009,7 @@ public class UtilityCommands { if (formatName != null) { final ClipboardFormat cf = ClipboardFormats.findByAlias(formatName); - forEachFile = new DelegateConsumer(forEachFile) { + forEachFile = new DelegateConsumer<>(forEachFile) { @Override public void accept(File file) { if (cf.isFormat(file)) { @@ -1024,7 +1018,7 @@ public class UtilityCommands { } }; } else { - forEachFile = new DelegateConsumer(forEachFile) { + forEachFile = new DelegateConsumer<>(forEachFile) { @Override public void accept(File file) { if (!file.toString().endsWith(".cached")) { @@ -1062,7 +1056,7 @@ public class UtilityCommands { } if (listGlobal) { File rel = MainUtil.resolveRelative(new File(dir, dirFilter)); - forEachFile = new DelegateConsumer(forEachFile) { + forEachFile = new DelegateConsumer<>(forEachFile) { @Override public void accept(File f) { try { @@ -1172,7 +1166,7 @@ public class UtilityCommands { StringBuilder name = new StringBuilder(); if (relative.isAbsolute()) { relative = root.toURI().relativize(file.toURI()); - name.append(".." + File.separator); + name.append("..").append(File.separator); } name.append(relative.getPath()); return name.toString(); From 362f6cc6e10b56032dcae6822e9ee42bc6c68ac9 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 22 May 2023 19:43:21 +0100 Subject: [PATCH 37/65] fix #2009 --- .../core/extension/factory/parser/mask/RichMaskParser.java | 1 + 1 file changed, 1 insertion(+) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java index f86645b67..4e0fbfa91 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java @@ -147,6 +147,7 @@ public class RichMaskParser extends FaweParser { try { builder.addRegex(full); } catch (InputParseException ignored) { + builder.clear(); context.setPreferringWildcard(false); context.setRestricted(false); BaseBlock block = worldEdit.getBlockFactory().parseFromInput(full, context); From 3041ff3e50fa420e41be1cb6232445175d3942fc Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 25 May 2023 18:06:20 +0100 Subject: [PATCH 38/65] feat: add -r (random rotate) flag to schem load and clipboard brush (#2244) - Implements #1873 --- .../worldedit/command/BrushCommands.java | 15 ++++---- .../worldedit/command/SchematicCommands.java | 14 +++++++- .../command/tool/brush/ClipboardBrush.java | 36 +++++++++++++++++++ 3 files changed, 58 insertions(+), 7 deletions(-) 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 2149951e0..d378decb1 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 @@ -1259,13 +1259,12 @@ public class BrushCommands { @Command( name = "clipboard", - desc = "@Deprecated use instead: `/br copypaste`)", + desc = "Paste your clipboard at the brush location. Includes any transforms.", descFooter = "Choose the clipboard brush.\n" + "Without the -o flag, the paste will appear centered at the target location. " + "With the flag, then the paste will appear relative to where you had " + "stood relative to the copied area when you copied it." ) - @Deprecated @CommandPermissions("worldedit.brush.clipboard") public void clipboardBrush( Player player, LocalSession session, @@ -1279,7 +1278,11 @@ public class BrushCommands { boolean pasteBiomes, @ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard") @ClipboardMask - Mask sourceMask, InjectedValueAccess context + Mask sourceMask, InjectedValueAccess context, + //FAWE start - random rotation + @Switch(name = 'r', desc = "Apply random rotation on paste, combines with existing clipboard transforms") + boolean randomRotate + //FAWE end ) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); @@ -1295,9 +1298,9 @@ public class BrushCommands { set( context, - new ClipboardBrush(newHolder, ignoreAir, usingOrigin, pasteEntities, pasteBiomes, - sourceMask - ), + //FAWE start - random rotation + new ClipboardBrush(newHolder, ignoreAir, usingOrigin, pasteEntities, pasteBiomes, sourceMask, randomRotate), + //FAWE end "worldedit.brush.clipboard" ); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 20dee10c3..c677734ef 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -45,6 +45,7 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.util.formatting.component.ErrorFormat; @@ -89,6 +90,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.Function; import java.util.regex.Pattern; @@ -312,7 +314,11 @@ public class SchematicCommands { @Arg(desc = "File name.") String filename, @Arg(desc = "Format name.", def = "fast") - String formatName + String formatName, + //FAWE start - random rotation + @Switch(name = 'r', desc = "Apply random rotation to the clipboard") + boolean randomRotate + //FAWE end ) throws FilenameException { LocalConfiguration config = worldEdit.getConfiguration(); @@ -394,6 +400,12 @@ public class SchematicCommands { uri = file.toURI(); } format.hold(actor, uri, in); + if (randomRotate) { + AffineTransform transform = new AffineTransform(); + int rotate = 90 * ThreadLocalRandom.current().nextInt(4); + transform = transform.rotateY(rotate); + session.getClipboard().setTransform(transform); + } actor.print(Caption.of("fawe.worldedit.schematic.schematic.loaded", filename)); } catch (IllegalArgumentException e) { actor.print(Caption.of("worldedit.schematic.unknown-filename", TextComponent.of(filename))); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index aac36df2f..b9c7dc45c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -27,9 +27,13 @@ import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.ClipboardHolder; +import java.util.concurrent.ThreadLocalRandom; + public class ClipboardBrush implements Brush { private final ClipboardHolder holder; @@ -38,6 +42,9 @@ public class ClipboardBrush implements Brush { private final boolean pasteEntities; private final boolean pasteBiomes; private final Mask sourceMask; + //FAWE start - random rotation + private final boolean randomRotate; + //FAWE end public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) { this.holder = holder; @@ -46,23 +53,48 @@ public class ClipboardBrush implements Brush { this.pasteBiomes = false; this.pasteEntities = false; this.sourceMask = null; + //FAWE start - random rotation + this.randomRotate = false; + //FAWE end } public ClipboardBrush( ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities, boolean pasteBiomes, Mask sourceMask ) { + //FAWE start - random rotation + this(holder, ignoreAirBlocks, usingOrigin, pasteEntities, pasteBiomes, sourceMask, false); + } + + public ClipboardBrush( + ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities, + boolean pasteBiomes, Mask sourceMask, boolean randomRotate + ) { + //FAWE end this.holder = holder; this.ignoreAirBlocks = ignoreAirBlocks; this.usingOrigin = usingOrigin; this.pasteEntities = pasteEntities; this.pasteBiomes = pasteBiomes; this.sourceMask = sourceMask; + this.randomRotate = true; } @Override public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws MaxChangedBlocksException { + //FAWE start - random rotation + Transform originalTransform = holder.getTransform(); + Transform transform = new AffineTransform(); + if (this.randomRotate) { + int rotate = 90 * ThreadLocalRandom.current().nextInt(4); + transform = ((AffineTransform) transform).rotateY(rotate); + if (originalTransform != null) { + transform = originalTransform.combine(transform); + } + } + holder.setTransform(transform); + //FAWE end Clipboard clipboard = holder.getClipboard(); Region region = clipboard.getRegion(); BlockVector3 centerOffset = region.getCenter().toBlockPoint().subtract(clipboard.getOrigin()); @@ -77,6 +109,10 @@ public class ClipboardBrush implements Brush { .build(); Operations.completeLegacy(operation); + //FAWE start - random rotation + // reset transform + holder.setTransform(originalTransform); + //FAWE end } } From a553961c05df12693a625f46e0587e0bc3e7c44b Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 27 May 2023 13:25:07 +0100 Subject: [PATCH 39/65] refactor: switch to EnumSet for heightmaps to improve performance (#2248) --- .../java/com/fastasyncworldedit/core/queue/IChunkSet.java | 4 ++-- .../core/queue/implementation/blocks/CharSetBlocks.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java index 79aed9f23..6d1f5c430 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java @@ -9,7 +9,7 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nullable; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -95,7 +95,7 @@ public interface IChunkSet extends IBlocks, OutputExtent { } default Map getHeightMaps() { - return new HashMap<>(); + return new EnumMap<>(HeightMapType.class); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index 291a3cf57..6f5129f4f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -15,7 +15,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; +import java.util.EnumMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -40,7 +40,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { public BlockVector3ChunkMap tiles; public HashSet entities; public HashSet entityRemoves; - public Map heightMaps; + public EnumMap heightMaps; private boolean fastMode = false; private int bitMask = -1; @@ -93,7 +93,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public Map getHeightMaps() { - return heightMaps == null ? new HashMap<>() : heightMaps; + return heightMaps == null ? new EnumMap<>(HeightMapType.class) : heightMaps; } @Override @@ -177,7 +177,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { @Override public void setHeightMap(HeightMapType type, int[] heightMap) { if (heightMaps == null) { - heightMaps = new HashMap<>(); + heightMaps = new EnumMap<>(HeightMapType.class); } heightMaps.put(type, heightMap); } From 03487718e7a293fe93457dba7ae1d2d25230406d Mon Sep 17 00:00:00 2001 From: Jordan Date: Wed, 31 May 2023 16:48:45 +0100 Subject: [PATCH 40/65] feat: configurable image hosts (#2243) --- .../core/configuration/Settings.java | 8 ++++++++ .../core/util/MainUtil.java | 19 ++++++++++++++++--- .../core/util/image/ImageUtil.java | 7 +++++-- .../worldedit/command/BrushCommands.java | 7 +++---- .../worldedit/command/GenerationCommands.java | 7 +++---- 5 files changed, 35 insertions(+), 13 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 50b515257..0c895adf9 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 @@ -671,6 +671,14 @@ public class Settings extends Config { }) public int MAX_IMAGE_SIZE = 8294400; + @Comment({ + "Whitelist of hostnames to allow images to be downloaded from", + " - Adding '*' to the list will allow any host, but this is NOT adviseable", + " - Crash exploits exist with malformed images", + " - See: https://medium.com/chargebee-engineering/perils-of-parsing-pixel-flood-attack-on-java-imageio-a97aeb06637d" + }) + public List ALLOWED_IMAGE_HOSTS = new ArrayList<>(Collections.singleton(("i.imgur.com"))); + } public static class EXTENT { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java index 8deb59d13..8122a840c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java @@ -52,6 +52,7 @@ import java.io.PrintWriter; import java.lang.reflect.Array; import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; @@ -68,7 +69,6 @@ import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -81,10 +81,8 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.regex.Pattern; -import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.GZIPInputStream; -import java.util.zip.Inflater; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -533,6 +531,21 @@ public class MainUtil { return readImage(new FileInputStream(file)); } + public static void checkImageHost(URI uri) throws IOException { + if (Settings.settings().WEB.ALLOWED_IMAGE_HOSTS.contains("*")) { + return; + } + String host = uri.getHost(); + if (Settings.settings().WEB.ALLOWED_IMAGE_HOSTS.stream().anyMatch(host::equalsIgnoreCase)) { + return; + } + throw new IOException(String.format( + "Host `%s` not allowed! Whitelisted image hosts are: %s", + host, + StringMan.join(Settings.settings().WEB.ALLOWED_IMAGE_HOSTS, ", ") + )); + } + public static BufferedImage toRGB(BufferedImage src) { if (src == null) { return src; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/image/ImageUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/image/ImageUtil.java index a8998e1f0..f99a17e6f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/image/ImageUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/image/ImageUtil.java @@ -203,6 +203,7 @@ public class ImageUtil { arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png"; } URL url = new URL(arg); + MainUtil.checkImageHost(url.toURI()); BufferedImage img = MainUtil.readImage(url); if (img == null) { throw new IOException("Failed to read " + url + ", please try again later"); @@ -218,7 +219,7 @@ public class ImageUtil { return MainUtil.readImage(file); } throw new InputParseException(Caption.of("fawe.error.invalid-image", TextComponent.of(arg))); - } catch (IOException e) { + } catch (IOException | URISyntaxException e) { throw new InputParseException(TextComponent.of(e.getMessage())); } } @@ -229,7 +230,9 @@ public class ImageUtil { if (arg.contains("imgur.com") && !arg.contains("i.imgur.com")) { arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png"; } - return new URL(arg).toURI(); + URI uri = new URI(arg); + MainUtil.checkImageHost(uri); + return uri; } if (arg.startsWith("file:/")) { arg = arg.replaceFirst("file:/+", ""); 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 d378decb1..358f75413 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 @@ -134,6 +134,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystems; import java.util.List; @@ -521,11 +522,9 @@ public class BrushCommands { @Switch(name = 'a', desc = "Use image Alpha") boolean alpha, @Switch(name = 'f', desc = "Blend the image with existing terrain") boolean fadeOut ) - throws WorldEditException, IOException { + throws WorldEditException, IOException, URISyntaxException { URL url = new URL(imageURL); - if (!url.getHost().equalsIgnoreCase("i.imgur.com")) { - throw new IOException("Only i.imgur.com links are allowed!"); - } + MainUtil.checkImageHost(url.toURI()); BufferedImage image = MainUtil.readImage(url); worldEdit.checkMaxBrushRadius(radius); if (yscale != 1) { 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 a1e6034f5..ca67ad132 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 @@ -65,6 +65,7 @@ import org.jetbrains.annotations.Range; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.concurrent.ExecutorService; @@ -580,12 +581,10 @@ public class GenerationCommands { @Arg(desc = "boolean", def = "true") boolean randomize, @Arg(desc = "TODO", def = "100") int threshold, @Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions - ) throws WorldEditException, IOException { + ) throws WorldEditException, IOException, URISyntaxException { TextureUtil tu = Fawe.instance().getCachedTextureUtil(randomize, 0, threshold); URL url = new URL(imageURL); - if (!url.getHost().equalsIgnoreCase("i.imgur.com")) { - throw new IOException("Only i.imgur.com links are allowed!"); - } + MainUtil.checkImageHost(url.toURI()); if (dimensions != null) { checkCommandArgument( (long) dimensions.getX() * dimensions.getZ() <= Settings.settings().WEB.MAX_IMAGE_SIZE, From 7ce17e5834dc5dad9f4286e5d61bf1a384c54277 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 09:23:17 +0200 Subject: [PATCH 41/65] Update dependency com.sk89q.worldguard:worldguard-bukkit to v7.0.8 (#2255) 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 6d1869213..388f45d9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ log4j = "2.19.0" # Plugins dummypermscompat = "1.10" -worldguard-bukkit = "7.0.7" +worldguard-bukkit = "7.0.8" mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" From 228e84e5552f6eacde25a3236925161d5373bd7a Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 1 Jun 2023 15:11:16 +0100 Subject: [PATCH 42/65] fix: Improve edit processing (#2247) --- .../com/fastasyncworldedit/core/Fawe.java | 5 +- .../core/configuration/Settings.java | 4 +- .../core/math/BlockVector3ChunkMap.java | 15 +- .../core/queue/IChunkSet.java | 12 + .../queue/implementation/QueueHandler.java | 3 +- .../SingleThreadQueueExtent.java | 16 +- .../implementation/blocks/CharSetBlocks.java | 68 ++- .../blocks/ThreadUnsafeCharBlocks.java | 536 ++++++++++++++++++ .../implementation/chunk/ChunkHolder.java | 2 +- 9 files changed, 638 insertions(+), 23 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java 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 edde8ed49..8b8e6c530 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -339,9 +339,10 @@ public class Fawe { Settings.settings().QUEUE.TARGET_SIZE, Settings.settings().QUEUE.PARALLEL_THREADS ); - if (Settings.settings().QUEUE.TARGET_SIZE < 2 * Settings.settings().QUEUE.PARALLEL_THREADS) { + if (Settings.settings().QUEUE.TARGET_SIZE < 4 * Settings.settings().QUEUE.PARALLEL_THREADS) { LOGGER.error( - "queue.target_size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" + ".target_size be at least twice queue.parallel_threads or higher.", + "queue.target_size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" + + ".target_size be at least four times queue.parallel_threads or greater.", Settings.settings().QUEUE.TARGET_SIZE, Settings.settings().QUEUE.PARALLEL_THREADS ); 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 0c895adf9..5eba1f01f 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 @@ -520,10 +520,10 @@ public class Settings extends Config { " - A smaller value will reduce memory usage", " - A value too small may break some operations (deform?)", " - Values smaller than the configurated parallel-threads are not accepted", - " - It is recommended this option be at least 2x greater than parallel-threads" + " - It is recommended this option be at least 4x greater than parallel-threads" }) - public int TARGET_SIZE = 64; + 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", diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java index 775f96e4d..a9f8a0210 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java @@ -9,7 +9,20 @@ import java.util.Map; public class BlockVector3ChunkMap implements IAdaptedMap { - private final Int2ObjectArrayMap map = new Int2ObjectArrayMap<>(); + private final Int2ObjectArrayMap map; + + public BlockVector3ChunkMap() { + map = new Int2ObjectArrayMap<>(); + } + + /** + * Create a new instance that is a copy of an existing map + * + * @param map existing map to copy + */ + public BlockVector3ChunkMap(BlockVector3ChunkMap map) { + this.map = new Int2ObjectArrayMap<>(map.getParent()); + } @Override public Map getParent() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java index 6d1f5c430..1af60b3a2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java @@ -8,6 +8,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.EnumMap; import java.util.Map; @@ -115,4 +116,15 @@ public interface IChunkSet extends IBlocks, OutputExtent { */ boolean hasBiomes(int layer); + /** + * Create an entirely distinct copy of this SET instance. All mutable data must be copied to sufficiently prevent leakage + * between the copy and the original. + * + * @return distinct new {@link IChunkSet instance} + */ + @Nonnull + default IChunkSet createCopy() { + return this; + } + } 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 49ebd84ca..2091a0bb9 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 @@ -53,7 +53,6 @@ public abstract class QueueHandler implements Trimable, Runnable { */ private long last; private long allocate = 50; - private double targetTPS = 18; public QueueHandler() { TaskManager.taskManager().repeat(this, 1); @@ -87,7 +86,7 @@ public abstract class QueueHandler implements Trimable, Runnable { private long getAllocate() { long now = System.currentTimeMillis(); - targetTPS = 18 - Math.max(Settings.settings().QUEUE.EXTRA_TIME_MS * 0.05, 0); + double targetTPS = 18 - Math.max(Settings.settings().QUEUE.EXTRA_TIME_MS * 0.05, 0); long diff = 50 + this.last - (this.last = now); long absDiff = Math.abs(diff); if (diff == 0) { 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 8ad257c76..a41d8786b 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 @@ -275,8 +275,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen * Get a new IChunk from either the pool, or create a new one
+ Initialize it at the * coordinates * - * @param chunkX - * @param chunkZ + * @param chunkX X chunk coordinate + * @param chunkZ Z chunk coordinate * @return IChunk */ private ChunkHolder poolOrCreate(int chunkX, int chunkZ) { @@ -309,19 +309,11 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen // If queueing is enabled AND either of the following // - memory is low & queue size > num threads + 8 // - queue size > target size and primary queue has less than num threads submissions - if (enabledQueue && ((lowMem && size > Settings.settings().QUEUE.PARALLEL_THREADS + 8) || (size > Settings.settings().QUEUE.TARGET_SIZE && Fawe - .instance() - .getQueueHandler() - .isUnderutilized()))) { + int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : Settings.settings().QUEUE.TARGET_SIZE; + if (enabledQueue && size > targetSize && (lowMem || Fawe.instance().getQueueHandler().isUnderutilized())) { chunk = chunks.removeFirst(); final Future future = submitUnchecked(chunk); if (future != null && !future.isDone()) { - final int targetSize; - if (lowMem) { - targetSize = Settings.settings().QUEUE.PARALLEL_THREADS + 8; - } else { - targetSize = Settings.settings().QUEUE.TARGET_SIZE; - } pollSubmissions(targetSize, lowMem); submissions.add(future); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index 6f5129f4f..b29c2f18e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -20,7 +20,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.stream.IntStream; public class CharSetBlocks extends CharBlocks implements IChunkSet { @@ -306,8 +305,12 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { || (heightMaps != null && !heightMaps.isEmpty())) { return false; } - //noinspection SimplifyStreamApiCallChains - this is faster than using #noneMatch - return !IntStream.range(minSectionPosition, maxSectionPosition + 1).anyMatch(this::hasSection); + for (int i = minSectionPosition; i <= maxSectionPosition; i++) { + if (hasSection(i)) { + return false; + } + } + return true; } @Override @@ -316,6 +319,9 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { tiles = null; entities = null; entityRemoves = null; + light = null; + skyLight = null; + heightMaps = null; super.reset(); return null; } @@ -329,6 +335,62 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { return biomes != null && biomes[layer] != null; } + @Override + public ThreadUnsafeCharBlocks createCopy() { + char[][] blocksCopy = new char[sectionCount][]; + for (int i = 0; i < sectionCount; i++) { + if (blocks[i] != null) { + blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; + System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER); + } + } + BiomeType[][] biomesCopy; + if (biomes == null) { + biomesCopy = null; + } else { + biomesCopy = new BiomeType[sectionCount][]; + for (int i = 0; i < sectionCount; i++) { + if (biomes[i] != null) { + biomesCopy[i] = new BiomeType[biomes[i].length]; + System.arraycopy(biomes[i], 0, biomesCopy[i], 0, biomes[i].length); + } + } + } + char[][] lightCopy = createLightCopy(light, sectionCount); + char[][] skyLightCopy = createLightCopy(skyLight, sectionCount); + return new ThreadUnsafeCharBlocks( + blocksCopy, + minSectionPosition, + maxSectionPosition, + biomesCopy, + sectionCount, + lightCopy, + skyLightCopy, + tiles != null ? new BlockVector3ChunkMap<>(tiles) : null, + entities != null ? new HashSet<>(entities) : null, + entityRemoves != null ? new HashSet<>(entityRemoves) : null, + heightMaps != null ? new EnumMap<>(heightMaps) : null, + defaultOrdinal(), + fastMode, + bitMask + ); + } + + static char[][] createLightCopy(char[][] lightArr, int sectionCount) { + if (lightArr == null) { + return null; + } else { + char[][] lightCopy = new char[sectionCount][]; + for (int i = 0; i < sectionCount; i++) { + if (lightArr[i] != null) { + lightCopy[i] = new char[lightArr[i].length]; + System.arraycopy(lightArr[i], 0, lightCopy[i], 0, lightArr[i].length); + } + } + return lightCopy; + } + } + @Override public char[] load(final int layer) { updateSectionIndexRange(layer); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java new file mode 100644 index 000000000..a423b3286 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -0,0 +1,536 @@ +package com.fastasyncworldedit.core.queue.implementation.blocks; + +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.FaweCache; +import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.math.BlockVector3ChunkMap; +import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunkSet; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Equivalent to {@link CharSetBlocks} without any attempt to make thread-safe for improved performance. + * This is currently only used as a "copy" of {@link CharSetBlocks} to provide to + * {@link com.fastasyncworldedit.core.queue.IBatchProcessor} instances for processing without overlapping the continuing edit. + * + * @since TODO + */ +public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private final char defaultOrdinal; + private char[][] blocks; + private int minSectionPosition; + private int maxSectionPosition; + private int sectionCount; + private BiomeType[][] biomes; + private char[][] light; + private char[][] skyLight; + private BlockVector3ChunkMap tiles; + private HashSet entities; + private HashSet entityRemoves; + private Map heightMaps; + private boolean fastMode; + private int bitMask; + + /** + * New instance given the data stored in a {@link CharSetBlocks} instance. + * + * @since TODO + */ + ThreadUnsafeCharBlocks( + char[][] blocks, + int minSectionPosition, + int maxSectionPosition, + BiomeType[][] biomes, + int sectionCount, + char[][] light, + char[][] skyLight, + BlockVector3ChunkMap tiles, + HashSet entities, + HashSet entityRemoves, + Map heightMaps, + char defaultOrdinal, + boolean fastMode, + int bitMask + ) { + this.blocks = blocks; + this.minSectionPosition = minSectionPosition; + this.maxSectionPosition = maxSectionPosition; + this.biomes = biomes; + this.sectionCount = sectionCount; + this.light = light; + this.skyLight = skyLight; + this.tiles = tiles; + this.entities = entities; + this.entityRemoves = entityRemoves; + this.heightMaps = heightMaps; + this.defaultOrdinal = defaultOrdinal; + this.fastMode = fastMode; + this.bitMask = bitMask; + } + + @Override + public boolean hasSection(int layer) { + layer -= minSectionPosition; + return layer >= 0 && layer < blocks.length && blocks[layer] != null && blocks[layer].length == FaweCache.INSTANCE.BLOCKS_PER_LAYER; + } + + @Override + public char[] load(int layer) { + updateSectionIndexRange(layer); + layer -= minSectionPosition; + return blocks[layer]; + } + + @Nullable + @Override + public char[] loadIfPresent(int layer) { + layer -= minSectionPosition; + return blocks[layer]; + } + + @Override + public Map getTiles() { + return tiles == null ? Collections.emptyMap() : tiles; + } + + @Override + public CompoundTag getTile(int x, int y, int z) { + return tiles.get(x, y, z); + } + + @Override + public Set getEntities() { + return entities == null ? Collections.emptySet() : entities; + } + + @Override + public Map getHeightMaps() { + return heightMaps == null ? new HashMap<>() : heightMaps; + } + + @Override + public void removeSectionLighting(int layer, boolean sky) { + updateSectionIndexRange(layer); + layer -= minSectionPosition; + if (light == null) { + light = new char[sectionCount][]; + } + if (light[layer] == null) { + light[layer] = new char[4096]; + } + Arrays.fill(light[layer], (char) 0); + if (sky) { + if (skyLight == null) { + skyLight = new char[sectionCount][]; + } + if (skyLight[layer] == null) { + skyLight[layer] = new char[4096]; + } + Arrays.fill(skyLight[layer], (char) 0); + } + } + + @Override + public boolean trim(boolean aggressive, int layer) { + return false; + } + + @Override + public int getSectionCount() { + return sectionCount; + } + + @Override + public int getMaxSectionPosition() { + return maxSectionPosition; + } + + @Override + public int getMinSectionPosition() { + return minSectionPosition; + } + + public char get(int x, int y, int z) { + int layer = (y >> 4); + if (!hasSection(layer)) { + return defaultOrdinal; + } + final int index = (y & 15) << 8 | z << 4 | x; + return blocks[layer - minSectionPosition][index]; + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + int layer; + if (biomes == null || (y >> 4) < minSectionPosition || (y >> 4) > maxSectionPosition) { + return null; + } else if (biomes[(layer = (y >> 4) - minSectionPosition)] == null) { + return null; + } + return biomes[layer][(y & 15) >> 2 | (z >> 2) << 2 | x >> 2]; + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypesCache.states[get(x, y, z)]; + } + + @Override + public boolean setBiome(int x, int y, int z, BiomeType biome) { + updateSectionIndexRange(y >> 4); + int layer = (y >> 4) - minSectionPosition; + if (biomes == null) { + biomes = new BiomeType[sectionCount][]; + biomes[layer] = new BiomeType[64]; + } else if (biomes[layer] == null) { + biomes[layer] = new BiomeType[64]; + } + biomes[layer][(y & 12) << 2 | (z & 12) | (x & 12) >> 2] = biome; + return true; + } + + @Override + public boolean setBiome(BlockVector3 position, BiomeType biome) { + return setBiome(position.getX(), position.getY(), position.getZ(), biome); + } + + public void set(int x, int y, int z, char value) { + final int layer = y >> 4; + final int index = (y & 15) << 8 | z << 4 | x; + try { + blocks[layer][index] = value; + } catch (ArrayIndexOutOfBoundsException exception) { + LOGGER.error("Tried setting block at coordinates (" + x + "," + y + "," + z + ")"); + assert Fawe.platform() != null; + LOGGER.error("Layer variable was = {}", layer, exception); + } + } + + @Override + public > boolean setBlock(int x, int y, int z, T holder) { + updateSectionIndexRange(y >> 4); + set(x, y, z, holder.getOrdinalChar()); + holder.applyTileEntity(this, x, y, z); + return true; + } + + @Override + public void setBlocks(int layer, char[] data) { + updateSectionIndexRange(layer); + layer -= minSectionPosition; + this.blocks[layer] = data; + } + + @Override + public boolean isEmpty() { + if (biomes != null + || light != null + || skyLight != null + || (entities != null && !entities.isEmpty()) + || (tiles != null && !tiles.isEmpty()) + || (entityRemoves != null && !entityRemoves.isEmpty()) + || (heightMaps != null && !heightMaps.isEmpty())) { + return false; + } + for (int i = minSectionPosition; i <= maxSectionPosition; i++) { + if (hasSection(i)) { + return false; + } + } + return true; + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) { + updateSectionIndexRange(y >> 4); + if (tiles == null) { + tiles = new BlockVector3ChunkMap<>(); + } + tiles.put(x, y, z, tile); + return true; + } + + @Override + public void setBlockLight(int x, int y, int z, int value) { + updateSectionIndexRange(y >> 4); + if (light == null) { + light = new char[sectionCount][]; + } + final int layer = (y >> 4) - minSectionPosition; + if (light[layer] == null) { + char[] c = new char[4096]; + Arrays.fill(c, (char) 16); + light[layer] = c; + } + final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15); + light[layer][index] = (char) value; + } + + @Override + public void setSkyLight(int x, int y, int z, int value) { + updateSectionIndexRange(y >> 4); + if (skyLight == null) { + skyLight = new char[sectionCount][]; + } + final int layer = (y >> 4) - minSectionPosition; + if (skyLight[layer] == null) { + char[] c = new char[4096]; + Arrays.fill(c, (char) 16); + skyLight[layer] = c; + } + final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15); + skyLight[layer][index] = (char) value; + } + + @Override + public void setHeightMap(HeightMapType type, int[] heightMap) { + if (heightMaps == null) { + heightMaps = new EnumMap<>(HeightMapType.class); + } + heightMaps.put(type, heightMap); + } + + @Override + public void setLightLayer(int layer, char[] toSet) { + updateSectionIndexRange(layer); + if (light == null) { + light = new char[sectionCount][]; + } + layer -= minSectionPosition; + light[layer] = toSet; + } + + @Override + public void setSkyLightLayer(int layer, char[] toSet) { + updateSectionIndexRange(layer); + if (skyLight == null) { + skyLight = new char[sectionCount][]; + } + layer -= minSectionPosition; + skyLight[layer] = toSet; + } + + @Override + public void setFullBright(int layer) { + updateSectionIndexRange(layer); + layer -= minSectionPosition; + if (light == null) { + light = new char[sectionCount][]; + } + if (light[layer] == null) { + light[layer] = new char[4096]; + } + if (skyLight == null) { + skyLight = new char[sectionCount][]; + } + if (skyLight[layer] == null) { + skyLight[layer] = new char[4096]; + } + Arrays.fill(light[layer], (char) 15); + Arrays.fill(skyLight[layer], (char) 15); + } + + @Override + public void setEntity(CompoundTag tag) { + if (entities == null) { + entities = new HashSet<>(); + } + entities.add(tag); + } + + @Override + public void removeEntity(UUID uuid) { + if (entityRemoves == null) { + entityRemoves = new HashSet<>(); + } + entityRemoves.add(uuid); + } + + @Override + public void setFastMode(boolean fastMode) { + this.fastMode = fastMode; + } + + @Override + public boolean isFastMode() { + return fastMode; + } + + @Override + public void setBitMask(int bitMask) { + this.bitMask = bitMask; + } + + @Override + public int getBitMask() { + return bitMask; + } + + @Override + public Set getEntityRemoves() { + return entityRemoves == null ? Collections.emptySet() : entityRemoves; + } + + @Override + public BiomeType[][] getBiomes() { + return biomes; + } + + @Override + public boolean hasBiomes() { + return IChunkSet.super.hasBiomes(); + } + + @Override + public char[][] getLight() { + return light; + } + + @Override + public char[][] getSkyLight() { + return skyLight; + } + + @Override + public boolean hasLight() { + return IChunkSet.super.hasLight(); + } + + @Override + public IChunkSet reset() { + blocks = new char[sectionCount][]; + biomes = new BiomeType[sectionCount][]; + light = new char[sectionCount][]; + skyLight = new char[sectionCount][]; + tiles.clear(); + entities.clear(); + entityRemoves.clear(); + heightMaps.clear(); + return this; + } + + @Override + public boolean hasBiomes(int layer) { + layer -= minSectionPosition; + return layer >= 0 && layer < biomes.length && biomes[layer] != null && biomes[layer].length > 0; + } + + @Override + public IChunkSet createCopy() { + char[][] blocksCopy = new char[sectionCount][]; + for (int i = 0; i < sectionCount; i++) { + if (blocks[i] != null) { + blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; + System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER); + } + } + BiomeType[][] biomesCopy; + if (biomes == null) { + biomesCopy = null; + } else { + biomesCopy = new BiomeType[sectionCount][]; + for (int i = 0; i < sectionCount; i++) { + if (biomes[i] != null) { + biomesCopy[i] = new BiomeType[biomes[i].length]; + System.arraycopy(biomes[i], 0, biomesCopy[i], 0, biomes[i].length); + } + } + } + char[][] lightCopy = CharSetBlocks.createLightCopy(light, sectionCount); + char[][] skyLightCopy = CharSetBlocks.createLightCopy(skyLight, sectionCount); + return new ThreadUnsafeCharBlocks( + blocksCopy, + minSectionPosition, + maxSectionPosition, + biomesCopy, + sectionCount, + lightCopy, + skyLightCopy, + tiles != null ? new BlockVector3ChunkMap<>(tiles) : null, + entities != null ? new HashSet<>(entities) : null, + entityRemoves != null ? new HashSet<>(entityRemoves) : null, + heightMaps != null ? new HashMap<>(heightMaps) : null, + defaultOrdinal, + fastMode, + bitMask + ); + } + + @Override + public boolean trim(boolean aggressive) { + return false; + } + + // Checks and updates the various section arrays against the new layer index + private void updateSectionIndexRange(int layer) { + if (layer >= minSectionPosition && layer <= maxSectionPosition) { + return; + } + if (layer < minSectionPosition) { + int diff = minSectionPosition - layer; + sectionCount += diff; + char[][] tmpBlocks = new char[sectionCount][]; + System.arraycopy(blocks, 0, tmpBlocks, diff, blocks.length); + blocks = tmpBlocks; + minSectionPosition = layer; + if (biomes != null) { + BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; + System.arraycopy(biomes, 0, tmpBiomes, diff, biomes.length); + biomes = tmpBiomes; + } + if (light != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(light, 0, tmplight, diff, light.length); + light = tmplight; + } + if (skyLight != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(skyLight, 0, tmplight, diff, skyLight.length); + skyLight = tmplight; + } + } else { + int diff = layer - maxSectionPosition; + sectionCount += diff; + char[][] tmpBlocks = new char[sectionCount][]; + System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length); + blocks = tmpBlocks; + maxSectionPosition = layer; + if (biomes != null) { + BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; + System.arraycopy(biomes, 0, tmpBiomes, 0, biomes.length); + biomes = tmpBiomes; + } + if (light != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(light, 0, tmplight, 0, light.length); + light = tmplight; + } + if (skyLight != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(skyLight, 0, tmplight, 0, skyLight.length); + skyLight = tmplight; + } + } + } + +} 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 9347ecf59..7390758bc 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 @@ -1044,7 +1044,7 @@ public class ChunkHolder> implements IQueueChunk { if (chunkSet != null && !chunkSet.isEmpty()) { chunkSet.setBitMask(bitMask); try { - return this.call(chunkSet, () -> { + return this.call(chunkSet.createCopy(), () -> { this.delegate = NULL; chunkSet = null; calledLock.unlock(stamp); From 03c4bafc4542e5478cd3385600223c4d8ac2afd8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 23:01:56 +0200 Subject: [PATCH 43/65] Update antlr4 to v4.13.0 (#2256) 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 388f45d9b..ff364ea0f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ auto-value = "1.10.1" findbugs = "3.0.2" rhino-runtime = "1.7.14" zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs -antlr4 = "4.12.0" +antlr4 = "4.13.0" json-simple = "1.1.1" jlibnoise = "1.0.0" jchronic = "0.2.4a" From 7646a067eb09e5999f4b0cf6f43fe7bd10efbbb7 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Fri, 2 Jun 2023 11:40:34 +0200 Subject: [PATCH 44/65] Avoid many threads blocking on AbstractChangeSet#processSet (#2226) --- .../history/changeset/AbstractChangeSet.java | 104 +++++++++++------- .../changeset/AbstractDelegateChangeSet.java | 2 - .../changeset/FaweStreamChangeSet.java | 2 +- 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index e52ef0cba..b06fc7926 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -39,19 +39,27 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; +/** + * This batch processor writes changes to a concrete implementation. + * {@link #processSet(IChunk, IChunkGet, IChunkSet)} is synchronized to guarantee consistency. + * To avoid many blocking threads on this method, changes are enqueued in {@link #queue}. + * This allows to keep other threads free for other work. + */ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { private static final Logger LOGGER = LogManagerCompat.getLogger(); private final World world; private final AtomicInteger lastException = new AtomicInteger(); - protected AtomicInteger waitingCombined = new AtomicInteger(0); - protected AtomicInteger waitingAsync = new AtomicInteger(0); - - protected boolean closed; + private final Semaphore workerSemaphore = new Semaphore(1, false); + private final ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + protected volatile boolean closed; public AbstractChangeSet(World world) { this.world = world; @@ -65,16 +73,11 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { if (closed) { return; } - waitingAsync.incrementAndGet(); TaskManager.taskManager().async(() -> { - waitingAsync.decrementAndGet(); - synchronized (waitingAsync) { - waitingAsync.notifyAll(); - } try { close(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.catching(e); } }); } @@ -82,20 +85,10 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { @Override public void flush() { try { - if (!Fawe.isMainThread()) { - while (waitingAsync.get() > 0) { - synchronized (waitingAsync) { - waitingAsync.wait(1000); - } - } - } - while (waitingCombined.get() > 0) { - synchronized (waitingCombined) { - waitingCombined.wait(1000); - } - } - } catch (InterruptedException e) { - e.printStackTrace(); + // drain with this thread too + drainQueue(true); + } catch (Exception e) { + LOGGER.catching(e); } } @@ -125,7 +118,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } @Override - public synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + public final synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { int bx = chunk.getX() << 4; int bz = chunk.getZ() << 4; @@ -306,12 +299,12 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { BaseBlock to = change.getCurrent(); add(loc, from, to); } catch (Exception e) { - e.printStackTrace(); + LOGGER.catching(e); } } public boolean isEmpty() { - return waitingCombined.get() == 0 && waitingAsync.get() == 0 && size() == 0; + return queue.isEmpty() && workerSemaphore.availablePermits() == 1 && size() == 0; } public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) { @@ -353,7 +346,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { add(x, y, z, combinedFrom, combinedTo); } catch (Exception e) { - e.printStackTrace(); + LOGGER.catching(e); } } @@ -362,7 +355,6 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } public Future addWriteTask(final Runnable writeTask, final boolean completeNow) { - AbstractChangeSet.this.waitingCombined.incrementAndGet(); Runnable wrappedTask = () -> { try { writeTask.run(); @@ -372,25 +364,55 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } else { int hash = t.getMessage().hashCode(); if (lastException.getAndSet(hash) != hash) { - t.printStackTrace(); - } - } - } finally { - if (AbstractChangeSet.this.waitingCombined.decrementAndGet() <= 0) { - synchronized (AbstractChangeSet.this.waitingAsync) { - AbstractChangeSet.this.waitingAsync.notifyAll(); - } - synchronized (AbstractChangeSet.this.waitingCombined) { - AbstractChangeSet.this.waitingCombined.notifyAll(); + LOGGER.catching(t); } } } }; if (completeNow) { wrappedTask.run(); - return Futures.immediateCancelledFuture(); + return Futures.immediateVoidFuture(); } else { - return Fawe.instance().getQueueHandler().submit(wrappedTask); + CompletableFuture task = new CompletableFuture<>(); + queue.add(() -> { + wrappedTask.run(); + task.complete(null); + }); + // make sure changes are processed + triggerWorker(); + return task; + } + } + + private void triggerWorker() { + if (workerSemaphore.availablePermits() == 0) { + return; // fast path to avoid additional tasks: a worker is already draining the queue + } + // create a new worker to drain the current queue + Fawe.instance().getQueueHandler().submit(() -> drainQueue(false)); + } + + private void drainQueue(boolean ignoreRunningState) { + if (!workerSemaphore.tryAcquire()) { + if (ignoreRunningState) { + // ignoreRunningState means we want to block + // even if another thread is already draining + try { + workerSemaphore.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } else { + return; // another thread is draining the queue already, ignore + } + } + try { + Runnable next; + while ((next = queue.poll()) != null) { // process all tasks in the queue + next.run(); + } + } finally { + workerSemaphore.release(); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java index ad3173fd6..195a5fdbd 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractDelegateChangeSet.java @@ -25,8 +25,6 @@ public class AbstractDelegateChangeSet extends AbstractChangeSet { public AbstractDelegateChangeSet(AbstractChangeSet parent) { super(parent.getWorld()); this.parent = parent; - this.waitingCombined = parent.waitingCombined; - this.waitingAsync = parent.waitingAsync; } public final AbstractChangeSet getParent() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java index c75a163f7..c2ae362ae 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java @@ -258,7 +258,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { if (blockSize > 0) { return false; } - if (waitingCombined.get() != 0 || waitingAsync.get() != 0) { + if (!super.isEmpty()) { return false; } flush(); From 82418155f63127ba85af9b3ef47b81ed444a36a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 12:21:16 +0000 Subject: [PATCH 45/65] Update dependency com.github.TownyAdvanced:Towny to v0.99.1.0 (#2254) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: dordsor21 --- gradle/libs.versions.toml | 4 ++-- worldedit-bukkit/build.gradle.kts | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff364ea0f..d3b4ba434 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.0.5" +towny = "0.99.1.0" # Third party bstats = "3.0.2" @@ -54,7 +54,7 @@ mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", ver griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" } griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" } residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" } -towny = { group = "com.github.TownyAdvanced", name = "Towny", version.ref = "towny" } +towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" } # Third Party bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" } diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 461d36c99..303e8fb0c 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -32,6 +32,10 @@ repositories { name = "OSS Sonatype Snapshots" url = uri("https://oss.sonatype.org/content/repositories/snapshots/") } + maven { + name = "Glaremasters" + url = uri("https://repo.glaremasters.me/repository/towny/") + } flatDir { dir(File("src/main/resources")) } } From 4fa927f996d63913436fbc50ee77f59af024f3a6 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Fri, 2 Jun 2023 19:39:00 +0200 Subject: [PATCH 46/65] Extract common code when resizing arrays (#2257) --- .../implementation/blocks/CharSetBlocks.java | 92 ++++++++----------- .../blocks/ThreadUnsafeCharBlocks.java | 60 +++++------- 2 files changed, 60 insertions(+), 92 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index b29c2f18e..9ab8734ea 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -410,67 +410,47 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { if (layer < minSectionPosition) { int diff = minSectionPosition - layer; sectionCount += diff; - char[][] tmpBlocks = new char[sectionCount][]; - Section[] tmpSections = new Section[sectionCount]; - Object[] tmpSectionLocks = new Object[sectionCount]; - System.arraycopy(blocks, 0, tmpBlocks, diff, blocks.length); - System.arraycopy(sections, 0, tmpSections, diff, sections.length); - System.arraycopy(sectionLocks, 0, tmpSectionLocks, diff, sections.length); - for (int i = 0; i < diff; i++) { - tmpSections[i] = EMPTY; - tmpSectionLocks[i] = new Object(); - } - blocks = tmpBlocks; - sections = tmpSections; - sectionLocks = tmpSectionLocks; minSectionPosition = layer; - if (biomes != null) { - BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; - System.arraycopy(biomes, 0, tmpBiomes, diff, biomes.length); - biomes = tmpBiomes; - } - if (light != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(light, 0, tmplight, diff, light.length); - light = tmplight; - } - if (skyLight != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(skyLight, 0, tmplight, diff, skyLight.length); - skyLight = tmplight; - } + resizeSectionsArrays(diff, false); // prepend new layer(s) } else { int diff = layer - maxSectionPosition; sectionCount += diff; - char[][] tmpBlocks = new char[sectionCount][]; - Section[] tmpSections = new Section[sectionCount]; - Object[] tmpSectionLocks = new Object[sectionCount]; - System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length); - System.arraycopy(sections, 0, tmpSections, 0, sections.length); - System.arraycopy(sectionLocks, 0, tmpSectionLocks, 0, sections.length); - for (int i = sectionCount - diff; i < sectionCount; i++) { - tmpSections[i] = EMPTY; - tmpSectionLocks[i] = new Object(); - } - blocks = tmpBlocks; - sections = tmpSections; - sectionLocks = tmpSectionLocks; maxSectionPosition = layer; - if (biomes != null) { - BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; - System.arraycopy(biomes, 0, tmpBiomes, 0, biomes.length); - biomes = tmpBiomes; - } - if (light != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(light, 0, tmplight, 0, light.length); - light = tmplight; - } - if (skyLight != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(skyLight, 0, tmplight, 0, skyLight.length); - skyLight = tmplight; - } + resizeSectionsArrays(diff, true); // append new layer(s) + } + } + + private void resizeSectionsArrays(int diff, boolean appendNew) { + char[][] tmpBlocks = new char[sectionCount][]; + Section[] tmpSections = new Section[sectionCount]; + Object[] tmpSectionLocks = new Object[sectionCount]; + int destPos = appendNew ? 0 : diff; + System.arraycopy(blocks, 0, tmpBlocks, destPos, blocks.length); + System.arraycopy(sections, 0, tmpSections, destPos, sections.length); + System.arraycopy(sectionLocks, 0, tmpSectionLocks, destPos, sections.length); + int toFillFrom = appendNew ? sectionCount - diff : 0; + int toFillTo = appendNew ? sectionCount : diff; + for (int i = toFillFrom; i < toFillTo; i++) { + tmpSections[i] = EMPTY; + tmpSectionLocks[i] = new Object(); + } + blocks = tmpBlocks; + sections = tmpSections; + sectionLocks = tmpSectionLocks; + if (biomes != null) { + BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; + System.arraycopy(biomes, 0, tmpBiomes, destPos, biomes.length); + biomes = tmpBiomes; + } + if (light != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(light, 0, tmplight, destPos, light.length); + light = tmplight; + } + if (skyLight != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(skyLight, 0, tmplight, destPos, skyLight.length); + skyLight = tmplight; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java index a423b3286..9f0aedc11 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -489,47 +489,35 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { if (layer < minSectionPosition) { int diff = minSectionPosition - layer; sectionCount += diff; - char[][] tmpBlocks = new char[sectionCount][]; - System.arraycopy(blocks, 0, tmpBlocks, diff, blocks.length); - blocks = tmpBlocks; minSectionPosition = layer; - if (biomes != null) { - BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; - System.arraycopy(biomes, 0, tmpBiomes, diff, biomes.length); - biomes = tmpBiomes; - } - if (light != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(light, 0, tmplight, diff, light.length); - light = tmplight; - } - if (skyLight != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(skyLight, 0, tmplight, diff, skyLight.length); - skyLight = tmplight; - } + resizeSectionsArrays(layer, diff, false); // prepend new layer(s) } else { int diff = layer - maxSectionPosition; sectionCount += diff; - char[][] tmpBlocks = new char[sectionCount][]; - System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length); - blocks = tmpBlocks; maxSectionPosition = layer; - if (biomes != null) { - BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; - System.arraycopy(biomes, 0, tmpBiomes, 0, biomes.length); - biomes = tmpBiomes; - } - if (light != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(light, 0, tmplight, 0, light.length); - light = tmplight; - } - if (skyLight != null) { - char[][] tmplight = new char[sectionCount][]; - System.arraycopy(skyLight, 0, tmplight, 0, skyLight.length); - skyLight = tmplight; - } + resizeSectionsArrays(layer, diff, true); // append new layer(s) + } + } + + private void resizeSectionsArrays(int layer, int diff, boolean appendNew) { + char[][] tmpBlocks = new char[sectionCount][]; + int destPos = appendNew ? 0 : diff; + System.arraycopy(blocks, 0, tmpBlocks, destPos, blocks.length); + blocks = tmpBlocks; + if (biomes != null) { + BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64]; + System.arraycopy(biomes, 0, tmpBiomes, destPos, biomes.length); + biomes = tmpBiomes; + } + if (light != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(light, 0, tmplight, destPos, light.length); + light = tmplight; + } + if (skyLight != null) { + char[][] tmplight = new char[sectionCount][]; + System.arraycopy(skyLight, 0, tmplight, destPos, skyLight.length); + skyLight = tmplight; } } From 048dcaf327d025eac80d7add6131b79fe55615f7 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 3 Jun 2023 16:34:23 +0100 Subject: [PATCH 47/65] fix: do not always set random rotate to true - Fixes #2259 - How'd that even get through --- .../com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index b9c7dc45c..e596298ea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -77,7 +77,7 @@ public class ClipboardBrush implements Brush { this.pasteEntities = pasteEntities; this.pasteBiomes = pasteBiomes; this.sourceMask = sourceMask; - this.randomRotate = true; + this.randomRotate = randomRotate; } @Override From c9a4a9c8b43acc041379133e35f67f27de0282af Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 4 Jun 2023 12:34:03 +0200 Subject: [PATCH 48/65] Update gradle to 8 (#2262) * Update gradle to 8 * Update codeql.yml --- .github/workflows/codeql.yml | 6 ++++++ buildSrc/build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 11 ++++++----- .../adapters/adapter-1_17_1/build.gradle.kts | 4 +++- .../adapters/adapter-1_18_2/build.gradle.kts | 4 +++- .../adapters/adapter-1_19/build.gradle.kts | 4 +++- .../adapters/adapter-1_19_3/build.gradle.kts | 4 +++- .../adapters/adapter-1_19_4/build.gradle.kts | 4 +++- worldedit-core/doctools/build.gradle.kts | 4 ++-- 11 files changed, 31 insertions(+), 14 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3c5eb079f..3b21f975b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,6 +18,12 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: temurin + cache: gradle + java-version: 17 - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 461e9deaa..de4d0df68 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -23,7 +23,7 @@ val properties = Properties().also { props -> dependencies { implementation(gradleApi()) implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0") - implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") + implementation("com.github.johnrengelman:shadow:8.1.1") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5") } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 13895 zcmZ8|Wmp``)-~=Hdu)0n3Y-8OvyK$p9^s9MM|Aj$miotNhy-{udLczZyd9uWtD)X_{|!LhIEF9y8(e*Z zW>^w$u&x|i9OjL=#6Nl~*ERulzX>8C-}o;iSMRYdfCU5d`~U{V4>HCg0HG4Xg2uP;fn!>S9+>LbuWbc0bETMQfo9~h}yI*TSv;Oikl~t-+xqI-`P$Rj@yi{mr2zC~s1snMT3!OPBdJ%IDnPXq+pl*Z>=+?qo${lkCSKmwTlVjfb3thU6B8yFjr!tphOs*G6 zwL`RyVAUXj4p=9&@PpWK)m+REuvHaq838TEhY^7W+JAp$ zZ^y;8`Z*@VqJ{sFFj?<|7SKS@G`$Yi)gx%nOi@Lr zCv0IJlFz0bP(eDIW(uWNq?;8zEAb+uGgnkLk;y!4XhA6=Eoa<`+|;6mOq>z`%ir@z$4)Mkd3 zF=hFo zyd{*bRQ4YUe^bU*Y`__)Uhu5NIjVJ~a}{lHp-_7wI?#EB11XcqmdY>pk`JJ) zW9Rt!tK=N>fZ!UDomwMnb`0EOvTjcNl=yW@$c!OAg*=l()GjZwSyJ+o^;Zi#I5*uP z$6qeih8&g8E(pNSneK>93A(8*%gvwv!0V|SqGcj55Y7`=N*@pJx_ig3uVuf-G~LJbm`7nxNcZ>Jgqy(LTHu_C2e>STp^Pm{}f&^)XU}vzuU`UV&>e& zqsXNXSs;Wri|?NhCq0vXC5$>9Cag$adyWz^x@NCiy2${9Dc)Y;J8k1Z933W$3$H}g zCQFU1XwzGm_WUheXvnDisH_%BdzMgNwk2^mHcQu*x>U%iN*B^8U(eVz1~(%`kV1Vb z=9T0xmN?bQMyrrd?u}jer}zV&sCK6zSm!zV8A8dP6THF=4*V{_K*E*K<)I(Q^(eV!m!vu##-2g|G z{RB;{gJB_X-w{ANq?ft_n!@=O8_gj6FxW&zO$7L3@NjWt@R{NxMbpHLk6;=2$0P5P=kKc1_85inX z#s$&s0zhV1cz>nRb#|D#N8Z-=Tphm)sGH>9cz3K3I)6XpimJW0(6$GtLzN(YPu9%R zdFXG9|30AZME4r@joC0IdvBBe08mF@+5Dd97p$h=n|pi80Cn2n{ev!S$llPGLqHva zZ3*OmW%!Qj>C$F!Ffafl7#I_1(gz!aa)b{ebU*=yH%^kr=~N?|2&2Df2o9X=2B?U!#R#+Cj45=f@=EcQx+9J z=X3~A=zbX29Fqn23m3dm}0Voj^Q9BjI=MiG+NZ)YCYn@r^qv(xE3=)&i z=(ML301=rNTptvUt2tnsPb1~G*DWFWoZfv)wV|uNW%?!)jju`jN(K-0$JYi!ofNup z9K%_ucHwutbZsl~vDQ!Jtj8uI6WA6K--@?8+_=t>g|kgUeC=w`IP9m&*fuoO3#A;t z&3@=3;J0>yjM89?h5MG$S`wW+=vyYOWQGhIP`^vScM8^JL{mGan5uTJPvAg$0z}8; z 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 J*WbtZ{{Z#P!zlm& delta 13442 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 508322917..37aef8d3f 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-7.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68d6..aeb74cbb4 100755 --- a/gradlew +++ b/gradlew @@ -85,9 +85,6 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +141,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts index 977b4af10..940dfcb0b 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension + applyPaperweightAdapterConfiguration() plugins { @@ -19,6 +21,6 @@ configurations.all { dependencies { - paperDevBundle("1.17.1-R0.1-20220414.034903-210") + the().paperDevBundle("1.17.1-R0.1-20220414.034903-210") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts index 2f8b75130..aaa4643d2 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension + plugins { java } @@ -10,6 +12,6 @@ repositories { dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - paperDevBundle("1.18.2-R0.1-20220920.010157-167") + the().paperDevBundle("1.18.2-R0.1-20220920.010157-167") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts index b8c797ee4..cfb2cfe03 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension + plugins { java } @@ -9,6 +11,6 @@ repositories { } dependencies { - paperDevBundle("1.19.2-R0.1-20221206.184705-189") + the().paperDevBundle("1.19.2-R0.1-20221206.184705-189") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts index fb422300e..4706a5752 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension + plugins { java } @@ -10,6 +12,6 @@ repositories { dependencies { // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - paperDevBundle("1.19.3-R0.1-20230312.180621-141") + the().paperDevBundle("1.19.3-R0.1-20230312.180621-141") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts index cf00f80f2..0df6dc795 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts @@ -1,3 +1,5 @@ +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension + plugins { java } @@ -9,6 +11,6 @@ repositories { } dependencies { - paperDevBundle("1.19.4-R0.1-20230423.020222-72") + the().paperDevBundle("1.19.4-R0.1-20230601.025018-99") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-core/doctools/build.gradle.kts b/worldedit-core/doctools/build.gradle.kts index 8f4555040..6f2123f20 100644 --- a/worldedit-core/doctools/build.gradle.kts +++ b/worldedit-core/doctools/build.gradle.kts @@ -1,14 +1,14 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.5.30" + kotlin("jvm") version "1.8.20" application } applyCommonConfiguration() tasks.withType { - kotlinOptions.jvmTarget = "11" + kotlinOptions.jvmTarget = "17" } application.mainClass.set("com.sk89q.worldedit.internal.util.DocumentationPrinter") From b7dc5f7ae51b49846f7844b5bccb52354524c6b1 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 4 Jun 2023 13:29:07 +0200 Subject: [PATCH 49/65] [ci skip] Update Java tooling name --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8c6ece0eb..61000e9fb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,7 +7,7 @@ pipeline { stage('Build') { steps { withEnv([ - "PATH+JAVA=${tool 'Temurin-17.0.6+10'}/bin" + "PATH+JAVA=${tool 'Temurin-17.0.7_7'}/bin" ]) { sh './gradlew clean build' } From 97d42441261a5226669f2ac774a41b9ec1b9029d Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 <62206933+EpicPlayerA10@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:14:50 +0200 Subject: [PATCH 50/65] Fix BlockEntity#remove spigot mapping (#2264) Fix spigot mapping --- .../adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 11dadaf1e..9f8938cec 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 @@ -184,7 +184,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { removeBlockEntityTicker.setAccessible(true); methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); - fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); + fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove.setAccessible(true); CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class); From 0e215e98d55fd2e8d588c88310503f9494414147 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 4 Jun 2023 16:22:21 +0100 Subject: [PATCH 51/65] fix: minor changes to fix loading blocks outside stored range in ThreadUnsafeCharBlocks (#2260) - Fixes #2258 --- .../implementation/blocks/ThreadUnsafeCharBlocks.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java index 9f0aedc11..7d041155b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -98,12 +98,19 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { public char[] load(int layer) { updateSectionIndexRange(layer); layer -= minSectionPosition; - return blocks[layer]; + char[] arr = blocks[layer]; + if (arr == null) { + arr = blocks[layer] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; + } + return arr; } @Nullable @Override public char[] loadIfPresent(int layer) { + if (layer < minSectionPosition || layer > maxSectionPosition) { + return null; + } layer -= minSectionPosition; return blocks[layer]; } @@ -439,8 +446,8 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { public IChunkSet createCopy() { char[][] blocksCopy = new char[sectionCount][]; for (int i = 0; i < sectionCount; i++) { + blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; if (blocks[i] != null) { - blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER]; System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER); } } From e9fed5a066fceb3117a920e4c1985e2f2647e5cc Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 4 Jun 2023 17:50:08 +0100 Subject: [PATCH 52/65] feat: improve error when loading biomes (#2241) --- .../v1_17_R1_2/PaperweightFaweAdapter.java | 16 ++++++++++---- .../fawe/v1_18_R2/PaperweightFaweAdapter.java | 15 +++++++++---- .../fawe/v1_19_R1/PaperweightFaweAdapter.java | 15 +++++++++---- .../fawe/v1_19_R2/PaperweightFaweAdapter.java | 15 +++++++++---- .../fawe/v1_19_R3/PaperweightFaweAdapter.java | 21 ++++++++++--------- 5 files changed, 56 insertions(+), 26 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/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java index d90fc50c8..64cf2c9d6 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java @@ -103,6 +103,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.OptionalInt; import java.util.Set; import java.util.function.Supplier; @@ -658,10 +659,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements .registryAccess() .ownedRegistryOrThrow( Registry.BIOME_REGISTRY); - return biomeRegistry.stream() - .map(biomeRegistry::getKey) - .map(CraftNamespacedKey::fromMinecraft) - .collect(Collectors.toList()); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java index 25f93844a..e2481e72b 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java @@ -656,10 +656,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements .registryAccess() .ownedRegistryOrThrow( Registry.BIOME_REGISTRY); - return biomeRegistry.stream() - .map(biomeRegistry::getKey).filter(Objects::nonNull) - .map(CraftNamespacedKey::fromMinecraft) - .collect(Collectors.toList()); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweAdapter.java index a2a593b5e..b9aa9cf7f 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweAdapter.java @@ -645,10 +645,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements .registryAccess() .ownedRegistryOrThrow( Registry.BIOME_REGISTRY); - return biomeRegistry.stream() - .map(biomeRegistry::getKey).filter(Objects::nonNull) - .map(CraftNamespacedKey::fromMinecraft) - .collect(Collectors.toList()); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java index af0bcfa32..7cfce755e 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java @@ -650,10 +650,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements .getServer() .registryAccess() .registryOrThrow(BIOME); - return biomeRegistry.stream() - .map(biomeRegistry::getKey).filter(Objects::nonNull) - .map(CraftNamespacedKey::fromMinecraft) - .collect(Collectors.toList()); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java index e23e18a2e..7cc904e5c 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java @@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; @@ -50,7 +49,6 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.item.ItemType; @@ -60,7 +58,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.WritableRegistry; import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.IntTag; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -71,14 +68,12 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.StringRepresentable; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -86,7 +81,6 @@ import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.TreeType; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState; @@ -602,10 +596,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements .getServer() .registryAccess() .registryOrThrow(BIOME); - return biomeRegistry.stream() - .map(biomeRegistry::getKey).filter(Objects::nonNull) - .map(CraftNamespacedKey::fromMinecraft) - .collect(Collectors.toList()); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; } @Override From 46e8a37cf09fdbb3c0a9b87b598f3f8578bf6d4b Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 4 Jun 2023 23:57:51 +0100 Subject: [PATCH 53/65] fix: improve test for polygonal region containment of cuboid - Possibly fixes #2265 --- .../worldedit/regions/Polygonal2DRegion.java | 32 ++++++++++--------- .../com/sk89q/worldedit/regions/Region.java | 2 -- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java index e02e72029..021e042be 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Polygonal2DRegion.java @@ -506,24 +506,26 @@ public class Polygonal2DRegion extends AbstractRegion implements FlatRegion { //FAWE start @Override public boolean containsEntireCuboid(int bx, int tx, int by, int ty, int bz, int tz) { - for (int x = bx; x <= tx; x++) { - if (!contains(x, 0, bz)) { - return false; + for (int y : new int[]{by, ty}) { + for (int x = bx; x <= tx; x++) { + if (!contains(x, y, bz)) { + return false; + } } - } - for (int x = bx; x <= tx; x++) { - if (!contains(x, 0, tz)) { - return false; + for (int x = bx; x <= tx; x++) { + if (!contains(x, y, tz)) { + return false; + } } - } - for (int z = bz; z <= tz; z++) { - if (!contains(bx, 0, z)) { - return false; + for (int z = bz; z <= tz; z++) { + if (!contains(bx, y, z)) { + return false; + } } - } - for (int z = bz; z <= tz; z++) { - if (!contains(tx, 0, z)) { - return false; + for (int z = bz; z <= tz; z++) { + if (!contains(tx, y, z)) { + return false; + } } } return true; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 8d718fb05..c18650560 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -42,8 +42,6 @@ import com.sk89q.worldedit.world.World; import javax.annotation.Nullable; import java.util.List; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; /** * Represents a physical shape. From e5d27c27bb1a4dbc98f70167dd5794cf7340761a Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 5 Jun 2023 00:17:13 +0100 Subject: [PATCH 54/65] chore: replace some hardcoded ordinals --- .../extent/clipboard/CPUOptimizedClipboard.java | 5 +++-- .../extent/clipboard/DiskOptimizedClipboard.java | 5 +++-- .../extent/clipboard/MemoryOptimizedClipboard.java | 5 +++-- .../extent/clipboard/io/FastSchematicWriter.java | 4 ++-- .../core/extent/filter/DistrFilter.java | 4 ++-- .../processor/heightmap/HeightmapProcessor.java | 4 ++-- .../core/history/change/MutableBiomeChange.java | 5 +++-- .../core/history/change/MutableFullBlockChange.java | 5 +++-- .../core/history/changeset/AbstractChangeSet.java | 2 +- .../core/queue/IBatchProcessor.java | 9 +++++---- .../com/sk89q/worldedit/regions/CuboidRegion.java | 13 +++++++------ .../java/com/sk89q/worldedit/regions/Region.java | 9 +++++---- 12 files changed, 39 insertions(+), 31 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java index 6f566fcb9..1a30a0df8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/CPUOptimizedClipboard.java @@ -11,6 +11,7 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.io.IOException; import java.util.Collection; @@ -191,8 +192,8 @@ public class CPUOptimizedClipboard extends LinearClipboard { @Override public > boolean setBlock(int index, B block) { char ordinal = block.getOrdinalChar(); - if (ordinal == 0) { - ordinal = 1; + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + ordinal = BlockTypesCache.ReservedIDs.AIR; } states[index] = ordinal; boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); 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 b54c075fc..08941d4a5 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 @@ -26,6 +26,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import org.apache.logging.log4j.Logger; import java.io.ByteArrayOutputStream; @@ -696,8 +697,8 @@ public class DiskOptimizedClipboard extends LinearClipboard { try { int index = headerSize + (getIndex(x, y, z) << 1); char ordinal = block.getOrdinalChar(); - if (ordinal == 0) { - ordinal = 1; + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + ordinal = BlockTypesCache.ReservedIDs.AIR; } byteBuffer.putChar(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java index ed76a9d1f..449795e5d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/MemoryOptimizedClipboard.java @@ -16,6 +16,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.io.IOException; import java.util.Collection; @@ -269,8 +270,8 @@ public class MemoryOptimizedClipboard extends LinearClipboard { @Override public > boolean setBlock(int index, B block) { int ordinal = block.getOrdinal(); - if (ordinal == 0) { - ordinal = 1; + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + ordinal = BlockTypesCache.ReservedIDs.AIR; } setOrdinal(index, ordinal); boolean hasNbt = block instanceof BaseBlock && block.hasNbtData(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicWriter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicWriter.java index 696dd3ef5..db3452d29 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicWriter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicWriter.java @@ -172,8 +172,8 @@ public class FastSchematicWriter implements ClipboardWriter { } int ordinal = block.getOrdinal(); - if (ordinal == 0) { - ordinal = 1; + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + ordinal = BlockTypesCache.ReservedIDs.AIR; } char value = palette[ordinal]; if (value == Character.MAX_VALUE) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/DistrFilter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/DistrFilter.java index 78a7ea293..89b294279 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/DistrFilter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/DistrFilter.java @@ -41,8 +41,8 @@ public class DistrFilter extends ForkedFilter { @Override public final void applyBlock(FilterBlock block) { int ordinal = block.getOrdinal(); - if (ordinal == 0) { - ordinal = 1; + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + ordinal = BlockTypesCache.ReservedIDs.AIR; } counter[ordinal]++; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java index efa2447f7..3cdfeafb9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightmapProcessor.java @@ -73,11 +73,11 @@ public class HeightmapProcessor implements IBatchProcessor { for (int y = 15; y >= 0; y--) { // We don't need to actually iterate over x and z as we're both reading and writing an index for (int j = 0; j < BLOCKS_PER_Y; j++) { - char ordinal = 0; + char ordinal = BlockTypesCache.ReservedIDs.__RESERVED__; if (hasSectionSet) { ordinal = setSection[index(y, j)]; } - if (ordinal == 0) { + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { if (!hasSectionGet) { if (!hasSectionSet) { continue layer; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBiomeChange.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBiomeChange.java index 46860c106..744a372e9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBiomeChange.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBiomeChange.java @@ -5,6 +5,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; public class MutableBiomeChange implements Change { @@ -13,8 +14,8 @@ public class MutableBiomeChange implements Change { private int to; public MutableBiomeChange() { - this.from = 0; - this.to = 0; + this.from = BlockTypesCache.ReservedIDs.__RESERVED__; + this.to = BlockTypesCache.ReservedIDs.__RESERVED__; } public void setBiome(int x, int y, int z, int from, int to) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java index 81337afb3..2b99cb8a9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java @@ -6,6 +6,7 @@ import com.sk89q.worldedit.extent.inventory.BlockBagException; import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; public class MutableFullBlockChange implements Change { @@ -39,14 +40,14 @@ public class MutableFullBlockChange implements Change { if (blockBag != null) { BlockState toState = BlockState.getFromOrdinal(to); if (fromState != toState) { - if (allowFetch && from != 0) { + if (allowFetch && from != BlockTypesCache.ReservedIDs.__RESERVED__) { try { blockBag.fetchPlacedBlock(fromState); } catch (BlockBagException e) { return; } } - if (allowStore && to != 0) { + if (allowStore && to != BlockTypesCache.ReservedIDs.__RESERVED__) { try { blockBag.storeDroppedBlock(toState); } catch (BlockBagException ignored) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index b06fc7926..bad1f96c6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -186,7 +186,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } final int combinedFrom = from; final int combinedTo = blocksSet[index]; - if (combinedTo != 0) { + if (combinedTo != BlockTypesCache.ReservedIDs.__RESERVED__) { add(xx, yy, zz, combinedFrom, combinedTo); } } 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 4ee815292..807a90fb4 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 @@ -7,6 +7,7 @@ import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; import java.util.Map; @@ -71,7 +72,7 @@ public interface IBatchProcessor { if (arr != null) { int index = (minY & 15) << 8; for (int i = 0; i < index; i++) { - arr[i] = 0; + arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } else { arr = new char[4096]; @@ -89,7 +90,7 @@ public interface IBatchProcessor { if (arr != null) { int index = ((maxY + 1) & 15) << 8; for (int i = index; i < arr.length; i++) { - arr[i] = 0; + arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } else { arr = new char[4096]; @@ -130,7 +131,7 @@ public interface IBatchProcessor { if (arr != null) { int index = (minY & 15) << 8; for (int i = index; i < 4096; i++) { - arr[i] = 0; + arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } set.setBlocks(layer, arr); @@ -139,7 +140,7 @@ public interface IBatchProcessor { if (arr != null) { int index = ((maxY + 1) & 15) << 8; for (int i = 0; i < index; i++) { - arr[i] = 0; + arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__; } } set.setBlocks(layer, arr); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 26f7636f4..213ee8473 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -31,6 +31,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.storage.ChunkStore; import javax.annotation.Nonnull; @@ -839,14 +840,14 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { for (int z = 0; z < lowerZ; z++) { // null the z values for (int x = 0; x < 16; x++, index++) { - arr[index] = 0; + arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; } } index = indexY + upperZi; for (int z = upperZ + 1; z < 16; z++) { // null the z values for (int x = 0; x < 16; x++, index++) { - arr[index] = 0; + arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; } } } @@ -855,11 +856,11 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { for (int z = lowerZ; z <= upperZ; z++, index += 16) { for (int x = 0; x < lowerX; x++) { // null the x values - arr[index + x] = 0; + arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; } for (int x = upperX + 1; x < 16; x++) { // null the x values - arr[index + x] = 0; + arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; } } } @@ -925,7 +926,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { for (int z = lowerZ; z <= upperZ; z++) { // null the z values for (int x = 0; x < 16; x++, index++) { - arr[index] = 0; + arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; } } } @@ -934,7 +935,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { for (int z = lowerZ; z <= upperZ; z++, index += 16) { for (int x = lowerX; x <= upperX; x++) { // null the x values - arr[index + x] = 0; + arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__; } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index c18650560..71227f2ba 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -38,6 +38,7 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockTypesCache; import javax.annotation.Nullable; import java.util.List; @@ -408,8 +409,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess for (int y = 0, index = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++, index++) { - if (arr[index] != 0 && !contains(x, y, z)) { - arr[index] = 0; + if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && !contains(x, y, z)) { + arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; } } } @@ -458,8 +459,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess for (int y = 0, index = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++, index++) { - if (arr[index] != 0 && contains(x, y, z)) { - arr[index] = 0; + if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && contains(x, y, z)) { + arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; processExtra = true; } } From 4b06796e7645fa1a2c8f0b064c5bf1114bd45133 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Mon, 5 Jun 2023 10:51:26 +0200 Subject: [PATCH 55/65] Archive PR artifacts (#2269) * Archive PR artifacts * Fix artifact path --- .github/workflows/build-pr.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 60c938e66..3959c8ad7 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -18,4 +18,9 @@ jobs: distribution: temurin java-version: 17 - name: Build on ${{ matrix.os }} - run: ./gradlew clean build + run: ./gradlew clean build --no-daemon + - name: Archive artifacts + uses: actions/upload-artifact@v3 + with: + name: FastAsyncWorldEdit-SNAPSHOT + path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar From 5da5b216f3ce36bf79e417c47285b89e65b699ac Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 5 Jun 2023 22:26:41 +0100 Subject: [PATCH 56/65] fix #2268 -.- --- .../src/main/java/com/sk89q/worldedit/regions/Region.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 71227f2ba..ab9207ab4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -409,7 +409,7 @@ public interface Region extends Iterable, Cloneable, IBatchProcess for (int y = 0, index = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++, index++) { - if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && !contains(x, y, z)) { + if (arr[index] != BlockTypesCache.ReservedIDs.__RESERVED__ && !contains(bx + x, by + y, bz + z)) { arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__; } } From bdb170a5ca5b6209f3ce48cea6961730c6c20686 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Tue, 6 Jun 2023 19:18:01 +0200 Subject: [PATCH 57/65] Explicit call repository task --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2879f6107..f56de9997 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -105,7 +105,7 @@ tasks { } nexusPublishing { - repositories { + this.repositories { sonatype { nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/")) snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/")) From 5504811f115b420c87e3dd80307fb0994214f3b2 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 6 Jun 2023 18:21:02 +0100 Subject: [PATCH 58/65] chore: minor improvements to processing-related code (#2271) --- .../impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java | 5 +---- .../adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java | 4 +--- .../adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java | 4 +--- .../adapter/impl/fawe/v1_19_R2/PaperweightGetBlocks.java | 4 +--- .../adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java | 4 +--- .../java/com/sk89q/worldedit/regions/CuboidRegion.java | 7 ++++--- .../src/main/java/com/sk89q/worldedit/regions/Region.java | 8 +++++++- 7 files changed, 16 insertions(+), 20 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 5554fe25b..09fa8f7a5 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,10 +445,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << layer; - // Changes may still be written to chunk SET - char[] tmp = set.load(layerNo); - char[] setArr = new char[4096]; - System.arraycopy(tmp, 0, setArr, 0, 4096); + char[] setArr = set.load(layerNo); // 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 f225bd441..06a74cfd1 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,9 +491,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] tmp = set.load(layerNo); - char[] setArr = new char[4096]; - System.arraycopy(tmp, 0, setArr, 0, 4096); + char[] setArr = set.load(layerNo); // 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/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java index 7a0b00f1f..e0a851c1b 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightGetBlocks.java @@ -488,9 +488,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] tmp = set.load(layerNo); - char[] setArr = new char[4096]; - System.arraycopy(tmp, 0, setArr, 0, 4096); + char[] setArr = set.load(layerNo); // 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_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightGetBlocks.java index f3114fd1a..3ee8314fd 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightGetBlocks.java @@ -490,9 +490,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] tmp = set.load(layerNo); - char[] setArr = new char[4096]; - System.arraycopy(tmp, 0, setArr, 0, 4096); + char[] setArr = set.load(layerNo); // 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 c12f57002..1b10ddd77 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,9 +490,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc bitMask |= 1 << getSectionIndex; - char[] tmp = set.load(layerNo); - char[] setArr = new char[4096]; - System.arraycopy(tmp, 0, setArr, 0, 4096); + char[] setArr = set.load(layerNo); // 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-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 213ee8473..cc2f03aa2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -824,14 +824,15 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { boolean trimX = lowerX != 0 || upperX != 15; boolean trimZ = lowerZ != 0 || upperZ != 15; + if (!(trimX || trimZ)) { + return set; + } + for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) { if (!set.hasSection(layer)) { continue; } char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true - if (!(trimX || trimZ)) { - continue; - } int indexY = 0; for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section int index; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index ab9207ab4..b89536520 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -401,11 +401,17 @@ public interface Region extends Iterable, Cloneable, IBatchProcess // contains some boolean processExtra = false; for (int layer = getMinimumY() >> 4; layer <= getMaximumY() >> 4; layer++) { + if (!set.hasSection(layer)) { + continue; + } int by = layer << 4; int ty = by + 15; if (!containsEntireCuboid(bx, tx, by, ty, bz, tz)) { processExtra = true; - char[] arr = set.load(layer); + char[] arr = set.loadIfPresent(layer); + if (arr == null) { + continue; + } for (int y = 0, index = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++, index++) { From b7719d17bd276c4c7dbc3d169c0dbc4ab236c471 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 6 Jun 2023 18:21:18 +0100 Subject: [PATCH 59/65] fix: correctly processSet with blacklist when required (#2270) * fix: correctly processSet with blacklist when required * Fix incorrect region --- .../com/fastasyncworldedit/core/extent/MultiRegionExtent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java index e7f88c7c5..c13c5965b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java @@ -172,7 +172,7 @@ public class MultiRegionExtent extends FaweRegionExtent { set = intersection.processSet(chunk, get, set); } if (disallowedIntersection != null) { - intersection.processSet(chunk, get, set); + set = disallowedIntersection.processSet(chunk, get, set, true); } return set; } From 84872cf9a26cc2b90119239fd1617b644936aa05 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 6 Jun 2023 18:22:25 +0100 Subject: [PATCH 60/65] chore: improve queue documentation and submit history to better queue (#2266) --- .../bukkit/adapter/BukkitQueueHandler.java | 6 +- .../history/changeset/AbstractChangeSet.java | 2 +- .../queue/implementation/QueueHandler.java | 219 +++++++++++++++++- .../core/util/TaskManager.java | 4 +- .../core/util/task/AsyncNotifyQueue.java | 13 +- .../task/FaweForkJoinWorkerThreadFactory.java | 21 ++ 6 files changed, 245 insertions(+), 20 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitQueueHandler.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitQueueHandler.java index 6e1bb1f9e..eec38e7ac 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitQueueHandler.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitQueueHandler.java @@ -3,8 +3,6 @@ package com.fastasyncworldedit.bukkit.adapter; import co.aikar.timings.Timings; import com.fastasyncworldedit.bukkit.listener.ChunkListener; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import org.apache.logging.log4j.Logger; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -31,7 +29,7 @@ public class BukkitQueueHandler extends QueueHandler { } @Override - public void startSet(boolean parallel) { + public void startUnsafe(boolean parallel) { ChunkListener.physicsFreeze = true; if (parallel) { try { @@ -51,7 +49,7 @@ public class BukkitQueueHandler extends QueueHandler { } @Override - public void endSet(boolean parallel) { + public void endUnsafe(boolean parallel) { ChunkListener.physicsFreeze = false; if (parallel) { try { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index bad1f96c6..b3493c896 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -389,7 +389,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { return; // fast path to avoid additional tasks: a worker is already draining the queue } // create a new worker to drain the current queue - Fawe.instance().getQueueHandler().submit(() -> drainQueue(false)); + Fawe.instance().getQueueHandler().async(() -> drainQueue(false)); } private void drainQueue(boolean ignoreRunningState) { 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 2091a0bb9..ae3ba4043 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 @@ -14,6 +14,7 @@ import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; +import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory; import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; import com.sk89q.worldedit.world.World; @@ -39,10 +40,41 @@ import java.util.function.Supplier; @SuppressWarnings({"unchecked", "rawtypes"}) public abstract class QueueHandler implements Trimable, Runnable { - private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool(); - private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(); + private static final int PROCESSORS = Runtime.getRuntime().availableProcessors(); + + /** + * 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. + */ + private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool( + PROCESSORS, + new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Primary - %s"), + 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. + */ + private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool( + PROCESSORS, + new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Secondary - %s"), + null, + false + ); + /** + * Main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order to forcibly + * prevent overworking/over-submission of chunk process tasks. + */ private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor(); + /** + * Queue for tasks to be completed on the main thread. These take priority of tasks submitted to syncWhenFree queue + */ private final ConcurrentLinkedQueue syncTasks = new ConcurrentLinkedQueue<>(); + /** + * Queue for tasks to be completed on the main thread. These are completed only if and when there is time left in a tick + * after completing all tasks in the syncTasks queue + */ private final ConcurrentLinkedQueue syncWhenFree = new ConcurrentLinkedQueue<>(); private final Map>> chunkGetCache = new HashMap<>(); @@ -54,7 +86,7 @@ public abstract class QueueHandler implements Trimable, Runnable { private long last; private long allocate = 50; - public QueueHandler() { + protected QueueHandler() { TaskManager.taskManager().repeat(this, 1); } @@ -80,6 +112,12 @@ public abstract class QueueHandler implements Trimable, Runnable { } } + /** + * Get if the {@code blockingExecutor} is saturated with tasks or not. Under-utilisation implies the queue has space for + * more submissions. + * + * @return true if {@code blockingExecutor} is not saturated with tasks + */ public boolean isUnderutilized() { return blockingExecutor.getActiveCount() < blockingExecutor.getMaximumPoolSize(); } @@ -125,6 +163,10 @@ public abstract class QueueHandler implements Trimable, Runnable { } while (System.currentTimeMillis() - start < currentAllocate); } + /** + * @deprecated For removal without replacement. + */ + @Deprecated(forRemoval = true, since = "TODO") public > void complete(Future task) { try { while (task != null) { @@ -135,49 +177,140 @@ public abstract class QueueHandler implements Trimable, Runnable { } } + /** + * Complete a task in the {@code forkJoinPoolSecondary} queue. 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. + * + * @param run Runnable to run + * @param value Value to return when done + * @param Value type + * @return Future for submitted task + */ public Future async(Runnable run, T value) { return forkJoinPoolSecondary.submit(run, value); } + /** + * Complete a task in the {@code forkJoinPoolSecondary} queue. 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. + * + * @param run Runnable to run + * @return Future for submitted task + */ public Future async(Runnable run) { return forkJoinPoolSecondary.submit(run); } + /** + * Complete a task in the {@code forkJoinPoolSecondary} queue. 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. + * + * @param call Callable to run + * @param Return value type + * @return Future for submitted task + */ public Future async(Callable call) { return forkJoinPoolSecondary.submit(call); } - public ForkJoinTask submit(Runnable call) { - return forkJoinPoolPrimary.submit(call); + /** + * Complete a task in the {@code forkJoinPoolPrimary} queue. 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. + * + * @param run Task to run + * @return {@link ForkJoinTask} representing task being run + */ + public ForkJoinTask submit(Runnable run) { + return forkJoinPoolPrimary.submit(run); } + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. + * + * @param run Task to run + * @param Value type + * @return Future representing task + */ public Future sync(Runnable run) { return sync(run, syncTasks); } + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. + * + * @param call Task to run + * @param Value type + * @return Future representing task + */ public Future sync(Callable call) throws Exception { return sync(call, syncTasks); } - public Future sync(Supplier call) { - return sync(call, syncTasks); + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. + * + * @param supplier Task to run + * @param Value type + * @return Future representing task + */ + public Future sync(Supplier supplier) { + return sync(supplier, syncTasks); } - // Lower priority sync task (runs only when there are no other tasks) + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * + * @param run Task to run + * @param value Value to return when done + * @param Value type + * @return Future representing task + */ public Future syncWhenFree(Runnable run, T value) { return sync(run, value, syncWhenFree); } + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * + * @param run Task to run + * @param Value type + * @return Future representing task + */ public Future syncWhenFree(Runnable run) { return sync(run, syncWhenFree); } + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * + * @param call Task to run + * @param Value type + * @return Future representing task + */ public Future syncWhenFree(Callable call) throws Exception { return sync(call, syncWhenFree); } - public Future syncWhenFree(Supplier call) { - return sync(call, syncWhenFree); + /** + * Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to + * maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed + * only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods. + * + * @param supplier Task to run + * @param Value type + * @return Future representing task + */ + public Future syncWhenFree(Supplier supplier) { + return sync(supplier, syncWhenFree); } private Future sync(Runnable run, T value, Queue queue) { @@ -228,6 +361,15 @@ public abstract class QueueHandler implements Trimable, Runnable { } } + /** + * Internal use only. Specifically for submitting {@link IQueueChunk} for "processing" an edit. Submits to the blocking + * executor, the main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order + * to forcibly prevent overworking/over-submission of chunk process tasks. + * + * @param chunk chunk + * @param + * @return Future representing task + */ public > T submit(IQueueChunk chunk) { // if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this // return (T) forkJoinPoolSecondary.submit(chunk); @@ -259,6 +401,9 @@ public abstract class QueueHandler implements Trimable, Runnable { return new SingleThreadQueueExtent(); } + /** + * Sets the current thread's {@link IQueueExtent} instance in the queue pool to null. + */ public void unCache() { queuePool.set(null); } @@ -271,14 +416,58 @@ public abstract class QueueHandler implements Trimable, Runnable { return queue; } - public abstract void startSet(boolean parallel); + /** + * Indicate a "set" task is being started. + * + * @param parallel if the "set" being started is parallel/async + * @deprecated To be replaced by better-named {@link QueueHandler#startUnsafe(boolean)} )} + */ + @Deprecated(forRemoval = true, since = "TODO") + public void startSet(boolean parallel) { + startUnsafe(parallel); + } - public abstract void endSet(boolean parallel); + /** + * Indicate a "set" task is ending. + * + * @param parallel if the "set" being started is parallel/async + * @deprecated To be replaced by better-named {@link QueueHandler#endUnsafe(boolean)} )} + */ + @Deprecated(forRemoval = true, since = "TODO") + public void endSet(boolean parallel) { + startUnsafe(parallel); + } + + /** + * Indicate an unsafe task is starting. Physics are frozen, async catchers disabled, etc. for the duration of the task + * + * @param parallel If the task is being run async and/or in parallel + */ + public abstract void startUnsafe(boolean parallel); + + /** + * Indicate a/the unsafe task submitted after a {@link QueueHandler#startUnsafe(boolean)} call has ended. + * + * @param parallel If the task was being run async and/or in parallel + */ + public abstract void endUnsafe(boolean parallel); + + /** + * Create a new queue for a given world. + */ public IQueueExtent getQueue(World world) { return getQueue(world, null, null); } + /** + * Create a new queue for a given world. + * + * @param world World to create queue for + * @param processor existing processor to set to queue or null + * @param postProcessor existing post-processor to set to queue or null + * @return New queue for given world + */ public IQueueExtent getQueue(World world, IBatchProcessor processor, IBatchProcessor postProcessor) { final IQueueExtent queue = pool(); IChunkCache cacheGet = getOrCreateWorldCache(world); @@ -293,6 +482,12 @@ public abstract class QueueHandler implements Trimable, Runnable { return queue; } + /** + * Trims each chunk GET cache + * + * @param aggressive if each chunk GET cache should be trimmed aggressively + * @return true if all chunk GET caches could be trimmed + */ @Override public boolean trim(boolean aggressive) { boolean result = true; 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 ad69c87d6..f3abf0ac3 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 @@ -157,13 +157,13 @@ public abstract class TaskManager { */ public void runUnsafe(Runnable run) { QueueHandler queue = Fawe.instance().getQueueHandler(); - queue.startSet(true); + queue.startUnsafe(Fawe.isMainThread()); try { run.run(); } catch (Throwable e) { e.printStackTrace(); } - queue.endSet(true); + queue.endUnsafe(Fawe.isMainThread()); } /** 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 10f910928..7cd91f87a 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,9 +1,13 @@ 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; @@ -11,6 +15,13 @@ 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; @@ -59,7 +70,7 @@ public class AsyncNotifyQueue implements Closeable { } return null; }; - self[0] = Fawe.instance().getQueueHandler().async(wrapped); + self[0] = QUEUE_SUBMISSIONS.submit(wrapped); return self[0]; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java new file mode 100644 index 000000000..589b5b863 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/FaweForkJoinWorkerThreadFactory.java @@ -0,0 +1,21 @@ +package com.fastasyncworldedit.core.util.task; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; + +public class FaweForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + + private final String nameFormat; + + public FaweForkJoinWorkerThreadFactory(String nameFormat) { + this.nameFormat = nameFormat; + } + + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + worker.setName(String.format(nameFormat, worker.getPoolIndex())); + return worker; + } + +} From 1356cd5caa5659f2615eef9d12f33f34a75fa7b6 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 6 Jun 2023 23:35:37 +0100 Subject: [PATCH 61/65] feat: implement a player-specific queue for clipboard IO tasks (#2267) * feat: implement a player-specific queue for clipboard IO tasks - Addresses #2222 (hopefully fixes) * Address comments * Add since --------- Co-authored-by: Alexander Brandes --- .../com/fastasyncworldedit/core/Fawe.java | 26 +++ .../clipboard/DiskOptimizedClipboard.java | 4 + .../util/task/KeyQueuedExecutorService.java | 172 ++++++++++++++++++ .../com/sk89q/worldedit/entity/Player.java | 13 +- 4 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java 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 8b8e6c530..7f31c6519 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -12,7 +12,9 @@ import com.fastasyncworldedit.core.util.RandomTextureUtil; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TextureUtil; import com.fastasyncworldedit.core.util.WEManager; +import com.fastasyncworldedit.core.util.task.KeyQueuedExecutorService; import com.github.luben.zstd.Zstd; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.internal.util.LogManagerCompat; import net.jpountz.lz4.LZ4Factory; @@ -33,6 +35,9 @@ import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.Date; import java.util.List; +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -86,6 +91,7 @@ public class Fawe { * The platform specific implementation. */ private final IFawe implementation; + private final KeyQueuedExecutorService clipboardExecutor; private FaweVersion version; private TextureUtil textures; private QueueHandler queueHandler; @@ -131,6 +137,15 @@ public class Fawe { }, 0); TaskManager.taskManager().repeat(timer, 1); + + clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor( + 1, + Settings.settings().QUEUE.PARALLEL_THREADS, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + new ThreadFactoryBuilder().setNameFormat("fawe-clipboard-%d").build() + )); } /** @@ -428,4 +443,15 @@ public class Fawe { return this.thread = Thread.currentThread(); } + /** + * Gets the executor used for clipboard IO if clipboard on disk is enabled or null + * + * @return Executor used for clipboard IO if clipboard on disk is enabled or null + * @since TODO + */ + @Nullable + public KeyQueuedExecutorService getClipboardExecutor() { + return this.clipboardExecutor; + } + } 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 08941d4a5..28dc52333 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 @@ -156,7 +156,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { /** * Load an existing file as a DiskOptimizedClipboard. The file MUST exist and MUST be created as a DiskOptimizedClipboard * with data written to it. + * @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)} */ + @Deprecated(forRemoval = true, since = "TODO") public DiskOptimizedClipboard(File file) { this(file, VERSION); } @@ -167,7 +169,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { * * @param file File to read from * @param versionOverride An override version to allow loading of older clipboards if required + * @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)} */ + @Deprecated(forRemoval = true, since = "TODO") public DiskOptimizedClipboard(File file, int versionOverride) { super(readSize(file, versionOverride), BlockVector3.ZERO); headerSize = getHeaderSizeOverrideFromVersion(versionOverride); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java new file mode 100644 index 000000000..bfaafb071 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java @@ -0,0 +1,172 @@ +package com.fastasyncworldedit.core.util.task; + +import javax.annotation.Nonnull; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Executor service that queues tasks based on keys, executing tasks on a configurable {@link ThreadPoolExecutor} + * + * @param Key type + * @since TODO + */ +public class KeyQueuedExecutorService { + + private final ExecutorService parent; + private final Map keyQueue = new HashMap<>(); + + /** + * Create a new {@link KeyQueuedExecutorService} instance + * + * @param parent Parent {@link ExecutorService} to use for actual task completion + */ + public KeyQueuedExecutorService(ExecutorService parent) { + this.parent = parent; + } + + /** + * Delegates to {@link ThreadPoolExecutor#shutdown()} + */ + public void shutdown() { + parent.shutdown(); + } + + /** + * Delegates to {@link ThreadPoolExecutor#shutdownNow()} + */ + @Nonnull + public List shutdownNow() { + return parent.shutdownNow(); + } + + /** + * Delegates to {@link ThreadPoolExecutor#isShutdown()} + */ + public boolean isShutdown() { + return parent.isShutdown(); + } + + /** + * Delegates to {@link ThreadPoolExecutor#isTerminated()} + */ + public boolean isTerminated() { + return parent.isTerminated(); + } + + /** + * Delegates to {@link ThreadPoolExecutor#awaitTermination(long, TimeUnit)} + */ + public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) throws InterruptedException { + return parent.awaitTermination(timeout, unit); + } + + protected FutureTask newTaskFor(Runnable runnable, T value) { + return new FutureTask<>(runnable, value); + } + + protected FutureTask newTaskFor(Callable callable) { + return new FutureTask<>(callable); + } + + @Nonnull + public Future submit(@Nonnull K key, @Nonnull Callable task) { + FutureTask ftask = newTaskFor(task); + execute(key, ftask); + return ftask; + } + + @Nonnull + public Future submit(@Nonnull K key, @Nonnull Runnable task, T result) { + FutureTask ftask = newTaskFor(task, result); + execute(key, ftask); + return ftask; + } + + @Nonnull + public Future submit(@Nonnull K key, @Nonnull Runnable task) { + FutureTask ftask = newTaskFor(task, null); + execute(key, ftask); + return ftask; + } + + public void execute(@Nonnull K key, @Nonnull FutureTask command) { + synchronized (keyQueue) { + boolean triggerRun = false; + KeyRunner runner = keyQueue.get(key); + if (runner == null) { + runner = new KeyRunner(key); + keyQueue.put(key, runner); + triggerRun = true; + } + runner.add(command); + if (triggerRun) { + runner.triggerRun(); + } + } + } + + private final class KeyRunner { + + private final Queue> tasks = new ConcurrentLinkedQueue<>(); + private final K key; + + private KeyRunner(K key) { + this.key = key; + } + + void add(FutureTask task) { + if (!tasks.add(task)) { + throw new RejectedExecutionException(rejection()); + } + } + + void triggerRun() { + Runnable task = tasks.poll(); + if (task == null) { + throw new RejectedExecutionException(rejection()); + } + try { + run(task); + } catch (RejectedExecutionException e) { + synchronized (keyQueue) { + keyQueue.remove(key); + } + throw new RejectedExecutionException(rejection(), e); + } + } + + private void run(Runnable task) { + parent.execute(() -> { + task.run(); + Runnable next = tasks.poll(); + if (next == null) { + synchronized (keyQueue) { + next = tasks.poll(); + if (next == null) { + keyQueue.remove(key); + } + } + } + if (next != null) { + run(next); + } + }); + } + + private String rejection() { + return "Task for the key '" + key + "' rejected"; + } + + } + +} 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 781b02ae2..8f5b5cb53 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 @@ -437,14 +437,16 @@ public interface Player extends Entity, Actor { } else { continue; } - WorldEdit.getInstance().getExecutorService().submit(() -> { + Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> { doc.close(); // Ensure closed before deletion doc.getFile().delete(); }); } } - } else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT || Settings.settings().CLIPBOARD.USE_DISK) { - WorldEdit.getInstance().getExecutorService().submit(() -> session.setClipboard(null)); + } else if (Settings.settings().CLIPBOARD.USE_DISK) { + Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> session.setClipboard(null)); + } else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) { + session.setClipboard(null); } if (Settings.settings().HISTORY.DELETE_ON_LOGOUT) { session.clearHistory(); @@ -470,7 +472,10 @@ public interface Player extends Entity, Actor { } } catch (EmptyClipboardException ignored) { } - DiskOptimizedClipboard doc = DiskOptimizedClipboard.loadFromFile(file); + DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit( + getUniqueId(), + () -> DiskOptimizedClipboard.loadFromFile(file) + ).get(); Clipboard clip = doc.toClipboard(); ClipboardHolder holder = new ClipboardHolder(clip); session.setClipboard(holder); From c5f66c342b433e735b1d3f659cb12c8b6e5eef07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:35:50 +0200 Subject: [PATCH 62/65] Update dependency com.palmergames.bukkit.towny:towny to v0.99.1.2 (#2272) 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 d3b4ba434..892d1c7cf 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.1.0" +towny = "0.99.1.2" # Third party bstats = "3.0.2" From 832f93c0f34816b45467cccc4327404d9139c8cd Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Wed, 7 Jun 2023 00:51:56 +0200 Subject: [PATCH 63/65] Optimize PR builds (#2273) * Optimize PR build * Update codeql.yml --- .github/workflows/build-pr.yml | 3 ++- .github/workflows/codeql.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 3959c8ad7..11f0d1d5d 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -16,9 +16,10 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin + cache: gradle java-version: 17 - name: Build on ${{ matrix.os }} - run: ./gradlew clean build --no-daemon + run: ./gradlew build -s - name: Archive artifacts uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3b21f975b..f4287607f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,5 +1,7 @@ name: "CodeQL" on: + push: + branches: [main] pull_request: # The branches below must be a subset of the branches above branches: [main] From 3b7d1267185939381212f8b370367b1772922e94 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Wed, 7 Jun 2023 11:11:54 +0200 Subject: [PATCH 64/65] Release 2.6.2 --- build.gradle.kts | 2 +- .../java/com/fastasyncworldedit/core/Fawe.java | 2 +- .../core/command/SuggestInputParseException.java | 14 +++++++------- .../extent/clipboard/DiskOptimizedClipboard.java | 4 ++-- .../core/queue/implementation/QueueHandler.java | 6 +++--- .../blocks/ThreadUnsafeCharBlocks.java | 4 ++-- .../core/util/task/KeyQueuedExecutorService.java | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f56de9997..a990b2904 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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/Fawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java index 7f31c6519..8f4818f2f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -447,7 +447,7 @@ public class Fawe { * Gets the executor used for clipboard IO if clipboard on disk is enabled or null * * @return Executor used for clipboard IO if clipboard on disk is enabled or null - * @since TODO + * @since 2.6.2 */ @Nullable public KeyQueuedExecutorService getClipboardExecutor() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java index bd10e6d20..fc50bf1a3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/SuggestInputParseException.java @@ -19,7 +19,7 @@ public class SuggestInputParseException extends InputParseException { /** * @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(Component, Supplier)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public SuggestInputParseException(String msg, String prefix, Supplier> getSuggestions) { this(new InputParseException(msg), getSuggestions); } @@ -27,7 +27,7 @@ public class SuggestInputParseException extends InputParseException { /** * @deprecated Use {@link SuggestInputParseException#of(Throwable, Supplier)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public static SuggestInputParseException of(Throwable other, String prefix, Supplier> getSuggestions) { if (other instanceof InputParseException) { return of((InputParseException) other, getSuggestions); @@ -38,7 +38,7 @@ public class SuggestInputParseException extends InputParseException { /** * @deprecated Use {@link SuggestInputParseException#of(InputParseException, Supplier)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public static SuggestInputParseException of(InputParseException other, String prefix, Supplier> getSuggestions) { if (other instanceof SuggestInputParseException) { return (SuggestInputParseException) other; @@ -49,7 +49,7 @@ public class SuggestInputParseException extends InputParseException { /** * @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(InputParseException, Supplier)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public SuggestInputParseException(InputParseException other, String prefix, Supplier> getSuggestions) { super(other.getRichMessage()); checkNotNull(getSuggestions); @@ -62,8 +62,8 @@ public class SuggestInputParseException extends InputParseException { * Create a new SuggestInputParseException instance * * @param message Message to send - * @param getSuggestions Supplier of list of sugegstions to give to user - * @since TODO + * @param getSuggestions Supplier of list of suggestions to give to user + * @since 2.6.2 */ public SuggestInputParseException(Component message, Supplier> getSuggestions) { this(new InputParseException(message), getSuggestions); @@ -129,7 +129,7 @@ public class SuggestInputParseException extends InputParseException { /** * @deprecated Unused */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public SuggestInputParseException prepend(String input) { // Do nothing return this; 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 28dc52333..a9704e36b 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 @@ -158,7 +158,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { * with data written to it. * @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public DiskOptimizedClipboard(File file) { this(file, VERSION); } @@ -171,7 +171,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { * @param versionOverride An override version to allow loading of older clipboards if required * @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public DiskOptimizedClipboard(File file, int versionOverride) { super(readSize(file, versionOverride), BlockVector3.ZERO); headerSize = getHeaderSizeOverrideFromVersion(versionOverride); 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 ae3ba4043..7f604a277 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 @@ -166,7 +166,7 @@ public abstract class QueueHandler implements Trimable, Runnable { /** * @deprecated For removal without replacement. */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public > void complete(Future task) { try { while (task != null) { @@ -422,7 +422,7 @@ public abstract class QueueHandler implements Trimable, Runnable { * @param parallel if the "set" being started is parallel/async * @deprecated To be replaced by better-named {@link QueueHandler#startUnsafe(boolean)} )} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public void startSet(boolean parallel) { startUnsafe(parallel); } @@ -434,7 +434,7 @@ public abstract class QueueHandler implements Trimable, Runnable { * @param parallel if the "set" being started is parallel/async * @deprecated To be replaced by better-named {@link QueueHandler#endUnsafe(boolean)} )} */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.6.2") public void endSet(boolean parallel) { startUnsafe(parallel); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java index 7d041155b..f3dda058f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/ThreadUnsafeCharBlocks.java @@ -30,7 +30,7 @@ import java.util.UUID; * This is currently only used as a "copy" of {@link CharSetBlocks} to provide to * {@link com.fastasyncworldedit.core.queue.IBatchProcessor} instances for processing without overlapping the continuing edit. * - * @since TODO + * @since 2.6.2 */ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { @@ -54,7 +54,7 @@ public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks { /** * New instance given the data stored in a {@link CharSetBlocks} instance. * - * @since TODO + * @since 2.6.2 */ ThreadUnsafeCharBlocks( char[][] blocks, diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java index bfaafb071..d9db5c888 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/task/KeyQueuedExecutorService.java @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; * Executor service that queues tasks based on keys, executing tasks on a configurable {@link ThreadPoolExecutor} * * @param Key type - * @since TODO + * @since 2.6.2 */ public class KeyQueuedExecutorService { From 9be1a58bf343c1a62c8e676f55f8e88d6c8e3bdc Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Wed, 7 Jun 2023 11:30:06 +0200 Subject: [PATCH 65/65] 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 a990b2904..f79edd650 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.6.2") +var rootVersion by extra("2.6.3") 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")