diff --git a/pom.xml b/pom.xml
index 5047cd7ca..c4be8f5b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,7 @@
2.0.9
3.12.8
- 1.18-R0.1-SNAPSHOT
+ 1.18.2-R0.1-SNAPSHOT
1.16.5-R0.1-SNAPSHOT
diff --git a/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java b/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java
deleted file mode 100644
index 40e0f4e97..000000000
--- a/src/main/java/world/bentobox/bentobox/nms/NMSAbstraction.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package world.bentobox.bentobox.nms;
-
-import org.bukkit.Chunk;
-import org.bukkit.block.data.BlockData;
-import org.bukkit.generator.ChunkGenerator;
-import org.bukkit.util.BoundingBox;
-
-public interface NMSAbstraction {
- /**
- * Copy the chunk data and biome grid to the given chunk.
- * @param chunk - chunk to copy to
- * @param chunkData - chunk data to copy
- * @param biomeGrid - biome grid to copy to
- * @param limitBox - bounding box to limit the copying
- */
- default void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
- double baseX = chunk.getX() << 4;
- double baseZ = chunk.getZ() << 4;
- int minHeight = chunk.getWorld().getMinHeight();
- int maxHeight = chunk.getWorld().getMaxHeight();
- for (int x = 0; x < 16; x++) {
- for (int z = 0; z < 16; z++) {
- if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
- continue;
- }
- for (int y = minHeight; y < maxHeight; y++) {
- setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
- // 3D biomes, 4 blocks separated
- if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
- chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
- }
- }
- }
- }
- }
-
- /**
- * Update the low-level chunk information for the given block to the new block ID and data. This
- * change will not be propagated to clients until the chunk is refreshed to them.
- * @param chunk - chunk to be changed
- * @param x - x coordinate within chunk 0 - 15
- * @param y - y coordinate within chunk 0 - world height, e.g. 255
- * @param z - z coordinate within chunk 0 - 15
- * @param blockData - block data to set the block
- * @param applyPhysics - apply physics or not
- */
- void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics);
-
-}
diff --git a/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java
new file mode 100644
index 000000000..86075a382
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/nms/SimpleWorldRegenerator.java
@@ -0,0 +1,137 @@
+package world.bentobox.bentobox.nms;
+
+import io.papermc.lib.PaperLib;
+import org.bukkit.Chunk;
+import org.bukkit.World;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.generator.ChunkGenerator;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.util.BoundingBox;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.addons.GameModeAddon;
+import world.bentobox.bentobox.database.objects.IslandDeletion;
+import world.bentobox.bentobox.util.MyBiomeGrid;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+
+public abstract class SimpleWorldRegenerator implements WorldRegenerator {
+ private final BentoBox plugin;
+
+ protected SimpleWorldRegenerator() {
+ this.plugin = BentoBox.getInstance();
+ }
+
+ /**
+ * Update the low-level chunk information for the given block to the new block ID and data. This
+ * change will not be propagated to clients until the chunk is refreshed to them.
+ *
+ * @param chunk - chunk to be changed
+ * @param x - x coordinate within chunk 0 - 15
+ * @param y - y coordinate within chunk 0 - world height, e.g. 255
+ * @param z - z coordinate within chunk 0 - 15
+ * @param blockData - block data to set the block
+ * @param applyPhysics - apply physics or not
+ */
+ protected abstract void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics);
+
+ @Override
+ public CompletableFuture regenerate(GameModeAddon gm, IslandDeletion di, World world) {
+ CompletableFuture bigFuture = new CompletableFuture<>();
+ new BukkitRunnable() {
+ private int chunkX = di.getMinXChunk();
+ private int chunkZ = di.getMinZChunk();
+ CompletableFuture currentTask = CompletableFuture.completedFuture(null);
+
+ @Override
+ public void run() {
+ if (!currentTask.isDone()) return;
+ if (isEnded(chunkX)) {
+ cancel();
+ bigFuture.complete(null);
+ return;
+ }
+ List> newTasks = new ArrayList<>();
+ for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
+ if (isEnded(chunkX)) {
+ break;
+ }
+ final int x = chunkX;
+ final int z = chunkZ;
+ newTasks.add(regenerateChunk(gm, di, world, x, z));
+ chunkZ++;
+ if (chunkZ > di.getMaxZChunk()) {
+ chunkZ = di.getMinZChunk();
+ chunkX++;
+ }
+ }
+ currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
+ }
+
+ private boolean isEnded(int chunkX) {
+ return chunkX > di.getMaxXChunk();
+ }
+ }.runTaskTimer(plugin, 0L, 20L);
+ return bigFuture;
+ }
+
+ @SuppressWarnings("deprecation")
+ private CompletableFuture regenerateChunk(GameModeAddon gm, IslandDeletion di, World world, int chunkX, int chunkZ) {
+ CompletableFuture chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ);
+ CompletableFuture invFuture = chunkFuture.thenAccept(chunk ->
+ Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
+ .filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
+ .forEach(te -> ((InventoryHolder) te).getInventory().clear())
+ );
+ CompletableFuture entitiesFuture = chunkFuture.thenAccept(chunk -> {
+ for (Entity e : chunk.getEntities()) {
+ if (!(e instanceof Player)) {
+ e.remove();
+ }
+ }
+ });
+ CompletableFuture copyFuture = chunkFuture.thenApply(chunk -> {
+ // Reset blocks
+ MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
+ ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
+ // Will be null if use-own-generator is set to true
+ if (cg != null) {
+ ChunkGenerator.ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
+ copyChunkDataToChunk(chunk, cd, grid, di.getBox());
+ }
+ return chunk;
+ });
+ CompletableFuture postCopyFuture = copyFuture.thenAccept(chunk -> {
+ // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
+ Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
+ });
+ return CompletableFuture.allOf(invFuture, entitiesFuture, postCopyFuture);
+ }
+
+ private void copyChunkDataToChunk(Chunk chunk, ChunkGenerator.ChunkData chunkData, ChunkGenerator.BiomeGrid biomeGrid, BoundingBox limitBox) {
+ double baseX = chunk.getX() << 4;
+ double baseZ = chunk.getZ() << 4;
+ int minHeight = chunk.getWorld().getMinHeight();
+ int maxHeight = chunk.getWorld().getMaxHeight();
+ for (int x = 0; x < 16; x++) {
+ for (int z = 0; z < 16; z++) {
+ if (!limitBox.contains(baseX + x, 0, baseZ + z)) {
+ continue;
+ }
+ for (int y = minHeight; y < maxHeight; y++) {
+ setBlockInNativeChunk(chunk, x, y, z, chunkData.getBlockData(x, y, z), false);
+ // 3D biomes, 4 blocks separated
+ if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) {
+ chunk.getBlock(x, y, z).setBiome(biomeGrid.getBiome(x, y, z));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/nms/WorldRegenerator.java b/src/main/java/world/bentobox/bentobox/nms/WorldRegenerator.java
new file mode 100644
index 000000000..a0bafb545
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/nms/WorldRegenerator.java
@@ -0,0 +1,22 @@
+package world.bentobox.bentobox.nms;
+
+import org.bukkit.World;
+import world.bentobox.bentobox.api.addons.GameModeAddon;
+import world.bentobox.bentobox.database.objects.IslandDeletion;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * A world generator used by {@link world.bentobox.bentobox.util.DeleteIslandChunks}
+ */
+public interface WorldRegenerator {
+ /**
+ * Create a future to regenerate the regions of the island.
+ *
+ * @param gm the game mode
+ * @param di the island deletion
+ * @param world the world
+ * @return the completable future
+ */
+ CompletableFuture regenerate(GameModeAddon gm, IslandDeletion di, World world);
+}
diff --git a/src/main/java/world/bentobox/bentobox/nms/fallback/NMSHandler.java b/src/main/java/world/bentobox/bentobox/nms/fallback/NMSHandler.java
deleted file mode 100644
index 23285dc4b..000000000
--- a/src/main/java/world/bentobox/bentobox/nms/fallback/NMSHandler.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package world.bentobox.bentobox.nms.fallback;
-
-import org.bukkit.Chunk;
-import org.bukkit.block.data.BlockData;
-
-import world.bentobox.bentobox.nms.NMSAbstraction;
-
-/**
- * @author tastybento
- *
- */
-public class NMSHandler implements NMSAbstraction {
-
- @Override
- public void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics) {
- chunk.getBlock(x, y, z).setBlockData(blockData, applyPhysics);
- }
-
-
-}
\ No newline at end of file
diff --git a/src/main/java/world/bentobox/bentobox/nms/fallback/WorldRegeneratorImpl.java b/src/main/java/world/bentobox/bentobox/nms/fallback/WorldRegeneratorImpl.java
new file mode 100644
index 000000000..60d356359
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/nms/fallback/WorldRegeneratorImpl.java
@@ -0,0 +1,20 @@
+package world.bentobox.bentobox.nms.fallback;
+
+import org.bukkit.Chunk;
+import org.bukkit.block.data.BlockData;
+
+import world.bentobox.bentobox.nms.SimpleWorldRegenerator;
+
+/**
+ * @author tastybento
+ *
+ */
+public class WorldRegeneratorImpl extends SimpleWorldRegenerator {
+
+ @Override
+ protected void setBlockInNativeChunk(Chunk chunk, int x, int y, int z, BlockData blockData, boolean applyPhysics) {
+ chunk.getBlock(x, y, z).setBlockData(blockData, applyPhysics);
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/world/bentobox/bentobox/nms/v1_18_R1/NMSHandler.java b/src/main/java/world/bentobox/bentobox/nms/v1_18_R2/WorldRegeneratorImpl.java
similarity index 78%
rename from src/main/java/world/bentobox/bentobox/nms/v1_18_R1/NMSHandler.java
rename to src/main/java/world/bentobox/bentobox/nms/v1_18_R2/WorldRegeneratorImpl.java
index f0585bde3..6754ecd02 100644
--- a/src/main/java/world/bentobox/bentobox/nms/v1_18_R1/NMSHandler.java
+++ b/src/main/java/world/bentobox/bentobox/nms/v1_18_R2/WorldRegeneratorImpl.java
@@ -1,19 +1,19 @@
-package world.bentobox.bentobox.nms.v1_18_R1;
+package world.bentobox.bentobox.nms.v1_18_R2;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
-import org.bukkit.craftbukkit.v1_18_R1.CraftWorld;
-import org.bukkit.craftbukkit.v1_18_R1.block.data.CraftBlockData;
+import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
import net.minecraft.core.BlockPosition;
import net.minecraft.world.level.World;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.chunk.Chunk;
-import world.bentobox.bentobox.nms.NMSAbstraction;
+import world.bentobox.bentobox.nms.SimpleWorldRegenerator;
-public class NMSHandler implements NMSAbstraction {
+public class WorldRegeneratorImpl extends SimpleWorldRegenerator {
private static final IBlockData AIR = ((CraftBlockData) Bukkit.createBlockData(Material.AIR)).getState();
diff --git a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java
index 0f1c88602..a9be0f62b 100644
--- a/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java
+++ b/src/main/java/world/bentobox/bentobox/util/DeleteIslandChunks.java
@@ -1,29 +1,16 @@
package world.bentobox.bentobox.util;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Chunk;
import org.bukkit.World;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Player;
-import org.bukkit.generator.ChunkGenerator;
-import org.bukkit.generator.ChunkGenerator.ChunkData;
-import org.bukkit.inventory.InventoryHolder;
-import org.bukkit.scheduler.BukkitTask;
-
-import io.papermc.lib.PaperLib;
+import org.bukkit.scheduler.BukkitRunnable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.events.island.IslandEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
import world.bentobox.bentobox.database.objects.IslandDeletion;
-import world.bentobox.bentobox.nms.NMSAbstraction;
+import world.bentobox.bentobox.nms.WorldRegenerator;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Deletes islands chunk by chunk
@@ -37,16 +24,10 @@ public class DeleteIslandChunks {
private final World netherWorld;
private final World endWorld;
private final AtomicBoolean completed;
- private final NMSAbstraction nms;
- private int chunkX;
- private int chunkZ;
- private BukkitTask task;
- private CompletableFuture currentTask = CompletableFuture.completedFuture(null);
+ private final WorldRegenerator regenerator;
public DeleteIslandChunks(BentoBox plugin, IslandDeletion di) {
this.plugin = plugin;
- this.chunkX = di.getMinXChunk();
- this.chunkZ = di.getMinZChunk();
this.di = di;
completed = new AtomicBoolean(false);
// Nether
@@ -61,9 +42,9 @@ public class DeleteIslandChunks {
} else {
endWorld = null;
}
- // NMS
- this.nms = Util.getNMS();
- if (nms == null) {
+ // Regenerator
+ this.regenerator = Util.getRegenerator();
+ if (regenerator == null) {
plugin.logError("Could not delete chunks because of NMS error");
return;
}
@@ -75,37 +56,23 @@ public class DeleteIslandChunks {
}
private void regenerateChunks() {
- // Run through all chunks of the islands and regenerate them.
- task = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
- if (!currentTask.isDone()) return;
- if (isEnded(chunkX)) {
- finish();
- return;
- }
- List> newTasks = new ArrayList<>();
- for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
- if (isEnded(chunkX)) {
- break;
- }
- final int x = chunkX;
- final int z = chunkZ;
- plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm -> {
- newTasks.add(processChunk(gm, di.getWorld(), x, z)); // Overworld
- newTasks.add(processChunk(gm, netherWorld, x, z)); // Nether
- newTasks.add(processChunk(gm, endWorld, x, z)); // End
- });
- chunkZ++;
- if (chunkZ > di.getMaxZChunk()) {
- chunkZ = di.getMinZChunk();
- chunkX++;
+ CompletableFuture all = plugin.getIWM().getAddon(di.getWorld())
+ .map(gm -> new CompletableFuture[]{
+ processWorld(gm, di.getWorld()), // Overworld
+ processWorld(gm, netherWorld), // Nether
+ processWorld(gm, endWorld) // End
+ })
+ .map(CompletableFuture::allOf)
+ .orElseGet(() -> CompletableFuture.completedFuture(null));
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (all.isDone()) {
+ finish();
+ cancel();
}
}
- currentTask = CompletableFuture.allOf(newTasks.toArray(new CompletableFuture[0]));
- }, 0L, 20L);
- }
-
- private boolean isEnded(int chunkX) {
- return chunkX > di.getMaxXChunk();
+ }.runTaskTimer(plugin, 0, 20);
}
private void finish() {
@@ -113,44 +80,16 @@ public class DeleteIslandChunks {
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
// We're done
completed.set(true);
- task.cancel();
}
- private CompletableFuture processChunk(GameModeAddon gm, World world, int x, int z) {
+ private CompletableFuture processWorld(GameModeAddon gm, World world) {
if (world != null) {
- return PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(gm, chunk, x, z));
+ return regenerator.regenerate(gm, di, world);
} else {
return CompletableFuture.completedFuture(null);
}
}
- private void regenerateChunk(GameModeAddon gm, Chunk chunk, int x, int z) {
- // Clear all inventories
- Arrays.stream(chunk.getTileEntities()).filter(InventoryHolder.class::isInstance)
- .filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
- .forEach(te -> ((InventoryHolder) te).getInventory().clear());
- // Remove all entities
- for (Entity e : chunk.getEntities()) {
- if (!(e instanceof Player)) {
- e.remove();
- }
- }
- // Reset blocks
- MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
- ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
- // Will be null if use-own-generator is set to true
- if (cg != null) {
- ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
- createChunk(cd, chunk, grid);
- }
- }
-
- private void createChunk(ChunkData cd, Chunk chunk, MyBiomeGrid grid) {
- nms.copyChunkDataToChunk(chunk, cd, grid, di.getBox());
- // Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
- Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
- }
-
public boolean isCompleted() {
return completed.get();
}
diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java
index f2ef681f8..c0a049119 100644
--- a/src/main/java/world/bentobox/bentobox/util/Util.java
+++ b/src/main/java/world/bentobox/bentobox/util/Util.java
@@ -47,7 +47,7 @@ import io.papermc.lib.PaperLib;
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
-import world.bentobox.bentobox.nms.NMSAbstraction;
+import world.bentobox.bentobox.nms.WorldRegenerator;
/**
* A set of utility methods
@@ -64,7 +64,7 @@ public class Util {
private static final String THE_END = "_the_end";
private static String serverVersion = null;
private static BentoBox plugin = BentoBox.getInstance();
- private static NMSAbstraction nms = null;
+ private static WorldRegenerator regenerator = null;
private Util() {}
@@ -689,23 +689,37 @@ public class Util {
}
/**
- * Set the NMS handler the plugin will use
- * @param nms the NMS handler
+ * Set the regenerator the plugin will use
+ * @param regenerator the regenerator
*/
- public static void setNms(NMSAbstraction nms) {
- Util.nms = nms;
+ public static void setRegenerator(WorldRegenerator regenerator) {
+ Util.regenerator = regenerator;
}
/**
- * Get the NMS handler the plugin will use
- * @return an NMS accelerated class for this server
+ * Get the regenerator the plugin will use
+ * @return an accelerated regenerator class for this server
*/
- public static NMSAbstraction getNMS() {
- if (nms == null) {
- plugin.log("No NMS Handler was set, falling back to Bukkit API.");
- setNms(new world.bentobox.bentobox.nms.fallback.NMSHandler());
+ public static WorldRegenerator getRegenerator() {
+ if (regenerator == null) {
+ String serverPackageName = Bukkit.getServer().getClass().getPackage().getName();
+ String pluginPackageName = plugin.getClass().getPackage().getName();
+ String version = serverPackageName.substring(serverPackageName.lastIndexOf('.') + 1);
+ WorldRegenerator handler;
+ try {
+ Class> clazz = Class.forName(pluginPackageName + ".nms." + version + ".WorldRegeneratorImpl");
+ if (WorldRegenerator.class.isAssignableFrom(clazz)) {
+ handler = (WorldRegenerator) clazz.getConstructor().newInstance();
+ } else {
+ throw new IllegalStateException("Class " + clazz.getName() + " does not implement WorldRegenerator");
+ }
+ } catch (Exception e) {
+ plugin.logWarning("No Regenerator found for " + version + ", falling back to Bukkit API.");
+ handler = new world.bentobox.bentobox.nms.fallback.WorldRegeneratorImpl();
+ }
+ setRegenerator(handler);
}
- return nms;
+ return regenerator;
}
/**