From a5de34dcfb690cf810c2fdd0405cbd5be866eb82 Mon Sep 17 00:00:00 2001 From: ch4ika Date: Fri, 23 Feb 2024 00:16:52 +0100 Subject: [PATCH 1/5] fix-count-entities --- .../bukkit/listener/EntityEventListener.java | 2 +- .../bukkit/util/BukkitRegionManager.java | 72 ++++--------------- .../java/com/plotsquared/core/plot/Plot.java | 16 ++--- 3 files changed, 20 insertions(+), 70 deletions(-) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java index 771738fb59..81f44f83af 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java @@ -195,7 +195,7 @@ public void creatureSpawnEvent(CreatureSpawnEvent event) { } return; } - if (BukkitEntityUtil.checkEntity(entity, plot.getBasePlot(false))) { + if (BukkitEntityUtil.checkEntity(entity, plot)) { event.setCancelled(true); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java index d78991c892..e899cafa7c 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java @@ -41,9 +41,7 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; -import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -51,9 +49,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import static com.plotsquared.core.util.entity.EntityCategories.CAP_ANIMAL; import static com.plotsquared.core.util.entity.EntityCategories.CAP_ENTITY; @@ -88,73 +84,29 @@ public boolean handleClear( @Override public int[] countEntities(@NonNull Plot plot) { + int[] count = new int[6]; int[] existing = (int[]) plot.getMeta("EntityCount"); if (existing != null && (System.currentTimeMillis() - (long) plot.getMeta("EntityCountTime") < 1000)) { return existing; } PlotArea area = plot.getArea(); World world = BukkitUtil.getWorld(area.getWorldName()); - Location bot = plot.getBottomAbs(); - Location top = plot.getTopAbs(); - int bx = bot.getX() >> 4; - int bz = bot.getZ() >> 4; - - int tx = top.getX() >> 4; - int tz = top.getZ() >> 4; - - int size = tx - bx << 4; - - Set chunks = new HashSet<>(); - for (int X = bx; X <= tx; X++) { - for (int Z = bz; Z <= tz; Z++) { - if (world.isChunkLoaded(X, Z)) { - chunks.add(world.getChunkAt(X, Z)); - } - } - } - - boolean doWhole = false; - List entities = null; - if (size > 200 && chunks.size() > 200) { - entities = world.getEntities(); - if (entities.size() < 16 + size / 8) { - doWhole = true; - } + if(world == null) { + return count; } - int[] count = new int[6]; - if (doWhole) { - for (Entity entity : entities) { - org.bukkit.Location location = entity.getLocation(); - PaperLib.getChunkAtAsync(location).thenAccept(chunk -> { - if (chunks.contains(chunk)) { - int X = chunk.getX(); - int Z = chunk.getZ(); - if (X > bx && X < tx && Z > bz && Z < tz) { - count(count, entity); - } else { - Plot other = area.getPlot(BukkitUtil.adapt(location)); - if (plot.equals(other)) { - count(count, entity); + for (final CuboidRegion region : plot.getRegions()) { + for (int x = region.getMinimumPoint().getX() >> 4; x <= region.getMaximumPoint().getX() >> 4; x++) { + for (int z = region.getMinimumPoint().getZ() >> 4; z <= region.getMaximumPoint().getZ() >> 4; z++) { + world.getChunkAtAsync(x, z).thenAccept(chunk -> { + final Entity[] entities = chunk.getEntities(); + for (Entity entity : entities) { + if (entity instanceof Player) { + continue; } - } - } - }); - } - } else { - for (Chunk chunk : chunks) { - int X = chunk.getX(); - int Z = chunk.getZ(); - Entity[] entities1 = chunk.getEntities(); - for (Entity entity : entities1) { - if (X == bx || X == tx || Z == bz || Z == tz) { - Plot other = area.getPlot(BukkitUtil.adapt(entity.getLocation())); - if (plot.equals(other)) { count(count, entity); } - } else { - count(count, entity); - } + }); } } } diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index 67db8a698e..a17398e0b7 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -1220,15 +1220,13 @@ public boolean removeFlag(final @NonNull PlotFlag flag) { */ public int[] countEntities() { int[] count = new int[6]; - for (Plot current : this.getConnectedPlots()) { - int[] result = this.regionManager.countEntities(current); - count[CAP_ENTITY] += result[CAP_ENTITY]; - count[CAP_ANIMAL] += result[CAP_ANIMAL]; - count[CAP_MONSTER] += result[CAP_MONSTER]; - count[CAP_MOB] += result[CAP_MOB]; - count[CAP_VEHICLE] += result[CAP_VEHICLE]; - count[CAP_MISC] += result[CAP_MISC]; - } + int[] result = this.regionManager.countEntities(this); + count[CAP_ENTITY] += result[CAP_ENTITY]; + count[CAP_ANIMAL] += result[CAP_ANIMAL]; + count[CAP_MONSTER] += result[CAP_MONSTER]; + count[CAP_MOB] += result[CAP_MOB]; + count[CAP_VEHICLE] += result[CAP_VEHICLE]; + count[CAP_MISC] += result[CAP_MISC]; return count; } From 9c6078c4b7ac0745b0f6ab0c7430246f621aaad4 Mon Sep 17 00:00:00 2001 From: ch4ika Date: Fri, 23 Feb 2024 09:25:05 +0100 Subject: [PATCH 2/5] Simplify array assignment --- .../java/com/plotsquared/core/plot/Plot.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index a17398e0b7..08be7d1fc5 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -100,13 +100,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_ANIMAL; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_ENTITY; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_MISC; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_MOB; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_MONSTER; -import static com.plotsquared.core.util.entity.EntityCategories.CAP_VEHICLE; - /** * The plot class
* [IMPORTANT] @@ -1219,15 +1212,7 @@ public boolean removeFlag(final @NonNull PlotFlag flag) { * @see RegionManager#countEntities(Plot) */ public int[] countEntities() { - int[] count = new int[6]; - int[] result = this.regionManager.countEntities(this); - count[CAP_ENTITY] += result[CAP_ENTITY]; - count[CAP_ANIMAL] += result[CAP_ANIMAL]; - count[CAP_MONSTER] += result[CAP_MONSTER]; - count[CAP_MOB] += result[CAP_MOB]; - count[CAP_VEHICLE] += result[CAP_VEHICLE]; - count[CAP_MISC] += result[CAP_MISC]; - return count; + return this.regionManager.countEntities(this); } /** From f8c71248f6083130f3fa2e2c302bb844606d6169 Mon Sep 17 00:00:00 2001 From: ch4ika Date: Fri, 23 Feb 2024 09:38:16 +0100 Subject: [PATCH 3/5] Fix entity filtering in chunk to match plot boundaries --- .../com/plotsquared/bukkit/util/BukkitRegionManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java index e899cafa7c..ec464a2763 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java @@ -104,7 +104,12 @@ public int[] countEntities(@NonNull Plot plot) { if (entity instanceof Player) { continue; } - count(count, entity); + + org.bukkit.Location location = entity.getLocation(); + Plot other = area.getPlot(BukkitUtil.adapt(location)); + if(plot.equals(other)) { + count(count, entity); + } } }); } From a120e49378a28a7d6b13030ccecfe79e07b3f6cb Mon Sep 17 00:00:00 2001 From: ch4ika Date: Fri, 23 Feb 2024 10:22:10 +0100 Subject: [PATCH 4/5] Fix asynchronous loading issue causing premature count return --- .../bukkit/util/BukkitRegionManager.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java index ec464a2763..7d1065e4fe 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java @@ -42,6 +42,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -91,30 +92,30 @@ public int[] countEntities(@NonNull Plot plot) { } PlotArea area = plot.getArea(); World world = BukkitUtil.getWorld(area.getWorldName()); - if(world == null) { + if (world == null) { return count; } for (final CuboidRegion region : plot.getRegions()) { for (int x = region.getMinimumPoint().getX() >> 4; x <= region.getMaximumPoint().getX() >> 4; x++) { for (int z = region.getMinimumPoint().getZ() >> 4; z <= region.getMaximumPoint().getZ() >> 4; z++) { - world.getChunkAtAsync(x, z).thenAccept(chunk -> { - final Entity[] entities = chunk.getEntities(); - for (Entity entity : entities) { - if (entity instanceof Player) { - continue; - } + Chunk chunk = world.getChunkAt(x, z); + final Entity[] entities = chunk.getEntities(); + for (Entity entity : entities) { + if (entity instanceof Player) { + continue; + } - org.bukkit.Location location = entity.getLocation(); - Plot other = area.getPlot(BukkitUtil.adapt(location)); - if(plot.equals(other)) { - count(count, entity); - } + org.bukkit.Location location = entity.getLocation(); + Plot other = area.getPlot(BukkitUtil.adapt(location)); + if (plot.equals(other)) { + count(count, entity); } - }); + } } } } + return count; } From 9720a9122b0c6cb554ae7d14ef916955c1bfb801 Mon Sep 17 00:00:00 2001 From: ch4ika Date: Fri, 23 Feb 2024 13:48:31 +0100 Subject: [PATCH 5/5] Implement asynchronous entity check --- .../bukkit/listener/EntityEventListener.java | 24 ++++++++++++------- .../bukkit/listener/EntitySpawnListener.java | 10 +++++--- .../bukkit/listener/PlayerEventListener.java | 10 +++++--- .../bukkit/util/BukkitEntityUtil.java | 5 ++++ .../bukkit/util/BukkitRegionManager.java | 9 ++++++- .../com/plotsquared/core/command/Caps.java | 17 ++++++------- .../java/com/plotsquared/core/plot/Plot.java | 4 ++++ .../plotsquared/core/util/RegionManager.java | 3 +++ 8 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java index 81f44f83af..eac33a6113 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntityEventListener.java @@ -43,6 +43,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.world.block.BlockType; import io.papermc.lib.PaperLib; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.World; @@ -195,9 +196,12 @@ public void creatureSpawnEvent(CreatureSpawnEvent event) { } return; } - if (BukkitEntityUtil.checkEntity(entity, plot)) { - event.setCancelled(true); - } + + BukkitEntityUtil.checkEntityAsync(entity, plot.getBasePlot(false)).thenAcceptAsync(toRemove -> { + if(toRemove) { + entity.remove(); + } + }, Bukkit.getScheduler().getMainThreadExecutor(BukkitPlatform.getPlugin(BukkitPlatform.class))); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @@ -421,13 +425,17 @@ public void onVehicleCreate(VehicleCreateEvent event) { return; } Plot plot = area.getOwnedPlotAbs(location); - if (plot == null || BukkitEntityUtil.checkEntity(entity, plot)) { - entity.remove(); + if (plot == null) { return; } - if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) { - entity.setMetadata("plot", new FixedMetadataValue((Plugin) PlotSquared.platform(), plot)); - } + + BukkitEntityUtil.checkEntityAsync(entity, plot.getBasePlot(false)).thenAcceptAsync(toRemove -> { + if (toRemove) { + entity.remove(); + } else if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) { + entity.setMetadata("plot", new FixedMetadataValue((Plugin) PlotSquared.platform(), plot)); + } + }, Bukkit.getScheduler().getMainThreadExecutor(BukkitPlatform.getPlugin(BukkitPlatform.class))); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntitySpawnListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntitySpawnListener.java index f1f05b6532..7a250f253c 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntitySpawnListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/EntitySpawnListener.java @@ -18,6 +18,7 @@ */ package com.plotsquared.bukkit.listener; +import com.plotsquared.bukkit.BukkitPlatform; import com.plotsquared.bukkit.util.BukkitEntityUtil; import com.plotsquared.bukkit.util.BukkitUtil; import com.plotsquared.core.PlotSquared; @@ -27,6 +28,7 @@ import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.flag.implementations.DoneFlag; import io.papermc.lib.PaperLib; +import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Block; @@ -155,9 +157,11 @@ public void creatureSpawnEvent(EntitySpawnEvent event) { event.setCancelled(true); } if (type == EntityType.ENDER_CRYSTAL) { - if (BukkitEntityUtil.checkEntity(entity, plot)) { - event.setCancelled(true); - } + BukkitEntityUtil.checkEntityAsync(entity, plot).thenAcceptAsync(toRemove -> { + if (toRemove) { + entity.remove(); + } + }, Bukkit.getScheduler().getMainThreadExecutor(BukkitPlatform.getPlugin(BukkitPlatform.class))); return; } if (type == EntityType.SHULKER) { diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java index 9fba3dff9d..e9d2fb8552 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/listener/PlayerEventListener.java @@ -21,6 +21,7 @@ import com.destroystokyo.paper.MaterialTags; import com.google.common.base.Charsets; import com.google.inject.Inject; +import com.plotsquared.bukkit.BukkitPlatform; import com.plotsquared.bukkit.player.BukkitPlayer; import com.plotsquared.bukkit.util.BukkitEntityUtil; import com.plotsquared.bukkit.util.BukkitUtil; @@ -1502,10 +1503,13 @@ public void onHangingPlace(HangingPlaceEvent event) { return; } } - if (BukkitEntityUtil.checkEntity(event.getEntity(), plot)) { - event.setCancelled(true); - } + Entity entity = event.getEntity(); + BukkitEntityUtil.checkEntityAsync(entity, plot.getBasePlot(false)).thenAcceptAsync(toRemove -> { + if (toRemove) { + entity.remove(); + } + }, Bukkit.getScheduler().getMainThreadExecutor(BukkitPlatform.getPlugin(BukkitPlatform.class))); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEntityUtil.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEntityUtil.java index becc3ddc1a..baa3c42851 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEntityUtil.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEntityUtil.java @@ -56,6 +56,7 @@ import org.bukkit.projectiles.ProjectileSource; import java.util.Objects; +import java.util.concurrent.CompletableFuture; public class BukkitEntityUtil { @@ -399,4 +400,8 @@ public static boolean checkEntity(Entity entity, Plot plot) { return EntityUtil.checkEntity(plot, EntityCapFlag.ENTITY_CAP_UNLIMITED); } + public static CompletableFuture checkEntityAsync(Entity entity, Plot plot) { + return CompletableFuture.supplyAsync(() -> checkEntity(entity, plot)); + } + } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java index 7d1065e4fe..445150bedf 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java @@ -45,12 +45,14 @@ import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; import static com.plotsquared.core.util.entity.EntityCategories.CAP_ANIMAL; import static com.plotsquared.core.util.entity.EntityCategories.CAP_ENTITY; @@ -102,7 +104,7 @@ public int[] countEntities(@NonNull Plot plot) { Chunk chunk = world.getChunkAt(x, z); final Entity[] entities = chunk.getEntities(); for (Entity entity : entities) { - if (entity instanceof Player) { + if (entity instanceof Player || entity instanceof Item) { continue; } @@ -119,6 +121,11 @@ public int[] countEntities(@NonNull Plot plot) { return count; } + @Override + public CompletableFuture countEntitiesAsync(final Plot plot) { + return CompletableFuture.supplyAsync(() -> countEntities(plot)); + } + @Override public boolean regenerateRegion( final @NonNull Location pos1, diff --git a/Core/src/main/java/com/plotsquared/core/command/Caps.java b/Core/src/main/java/com/plotsquared/core/command/Caps.java index dd3cf7ec84..6a331ab703 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Caps.java +++ b/Core/src/main/java/com/plotsquared/core/command/Caps.java @@ -64,14 +64,15 @@ public boolean onCommand(final PlotPlayer player, final String[] args) { player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large")); return false; } - player.sendMessage(TranslatableCaption.of("info.plot_caps_header")); - final int[] countedEntities = plot.countEntities(); - sendFormatted(plot, player, MobCapFlag.class, countedEntities, "mobs", CAP_MOB); - sendFormatted(plot, player, HostileCapFlag.class, countedEntities, "hostile", CAP_MONSTER); - sendFormatted(plot, player, AnimalCapFlag.class, countedEntities, "animals", CAP_ANIMAL); - sendFormatted(plot, player, VehicleCapFlag.class, countedEntities, "vehicle", CAP_VEHICLE); - sendFormatted(plot, player, MiscCapFlag.class, countedEntities, "misc", CAP_MISC); - sendFormatted(plot, player, EntityCapFlag.class, countedEntities, "entities", CAP_ENTITY); + plot.countEntitiesAsync().thenAccept(countedEntities -> { + player.sendMessage(TranslatableCaption.of("info.plot_caps_header")); + sendFormatted(plot, player, MobCapFlag.class, countedEntities, "mobs", CAP_MOB); + sendFormatted(plot, player, HostileCapFlag.class, countedEntities, "hostile", CAP_MONSTER); + sendFormatted(plot, player, AnimalCapFlag.class, countedEntities, "animals", CAP_ANIMAL); + sendFormatted(plot, player, VehicleCapFlag.class, countedEntities, "vehicle", CAP_VEHICLE); + sendFormatted(plot, player, MiscCapFlag.class, countedEntities, "misc", CAP_MISC); + sendFormatted(plot, player, EntityCapFlag.class, countedEntities, "entities", CAP_ENTITY); + }); return true; } diff --git a/Core/src/main/java/com/plotsquared/core/plot/Plot.java b/Core/src/main/java/com/plotsquared/core/plot/Plot.java index 08be7d1fc5..807b78d133 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -1215,6 +1215,10 @@ public int[] countEntities() { return this.regionManager.countEntities(this); } + public CompletableFuture countEntitiesAsync() { + return this.regionManager.countEntitiesAsync(this); + } + /** * Returns true if a previous task was running * diff --git a/Core/src/main/java/com/plotsquared/core/util/RegionManager.java b/Core/src/main/java/com/plotsquared/core/util/RegionManager.java index 765bdc7453..4f9d47c733 100644 --- a/Core/src/main/java/com/plotsquared/core/util/RegionManager.java +++ b/Core/src/main/java/com/plotsquared/core/util/RegionManager.java @@ -48,6 +48,7 @@ import java.io.File; import java.util.Collection; import java.util.Set; +import java.util.concurrent.CompletableFuture; public abstract class RegionManager { @@ -88,6 +89,8 @@ public static BlockVector2 getRegion(Location location) { */ public abstract int[] countEntities(Plot plot); + public abstract CompletableFuture countEntitiesAsync(Plot plot); + public void deleteRegionFiles(final String world, final Collection chunks, final Runnable whenDone) { TaskManager.runTaskAsync(() -> { for (BlockVector2 loc : chunks) {