diff --git a/src/main/java/me/youhavetrouble/blockedit/WorkSplitter.java b/src/main/java/me/youhavetrouble/blockedit/WorkSplitter.java index 6e2d3f3..cfde6ec 100644 --- a/src/main/java/me/youhavetrouble/blockedit/WorkSplitter.java +++ b/src/main/java/me/youhavetrouble/blockedit/WorkSplitter.java @@ -1,9 +1,18 @@ package me.youhavetrouble.blockedit; +import me.youhavetrouble.blockedit.api.BlockEditOperation; import me.youhavetrouble.blockedit.util.ChunkWork; +import me.youhavetrouble.blockedit.util.Selection; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.util.BoundingBox; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; public class WorkSplitter { @@ -21,10 +30,36 @@ public class WorkSplitter { return chunks; } - public static ChunkWork locationToChunkWork(double x, double z) { - int chunkX = (int) x >> 4; - int chunkZ = (int) z >> 4; - return new ChunkWork(chunkX, chunkZ); + public static void runOperation(HashSet chunkWorks, Selection selection, int chunksPerTick, BlockEditOperation operation) { + if (selection == null) return; + List chunkWork = new ArrayList<>(chunkWorks); + AtomicInteger element = new AtomicInteger(chunkWork.size()-1); + Bukkit.getScheduler().runTaskTimer(BlockEdit.getPlugin(), (task) -> { + if (element.get() < 0) { + task.cancel(); + return; + } + for (int i = 0; i< chunksPerTick; i++) { + processChunkWork(chunkWork.get(element.getAndDecrement()), selection, operation); + } + }, 0, 1); + } + + private static void processChunkWork(ChunkWork chunkWork, Selection selection, BlockEditOperation operation) { + World world = selection.getWorld(); + if (world == null) return; + chunkWork.getChunkAsync(world).thenAccept(chunk -> { + // skip y levels that are not in the selection + for (int y = (int) selection.getMinY(); y <= selection.getMaxY(); y++) { + for (int x = 0; x <= 15; x++) { + for (int z = 0; z <= 15; z++) { + Block block = chunk.getBlock(x, y, z); + if (!selection.contains(block.getLocation().toVector())) continue; + operation.transformBlock(block); + } + } + } + }); } } diff --git a/src/main/java/me/youhavetrouble/blockedit/api/BlockEditAPI.java b/src/main/java/me/youhavetrouble/blockedit/api/BlockEditAPI.java new file mode 100644 index 0000000..31831e2 --- /dev/null +++ b/src/main/java/me/youhavetrouble/blockedit/api/BlockEditAPI.java @@ -0,0 +1,21 @@ +package me.youhavetrouble.blockedit.api; + +import me.youhavetrouble.blockedit.WorkSplitter; +import me.youhavetrouble.blockedit.util.ChunkWork; +import me.youhavetrouble.blockedit.util.Selection; + +import java.util.HashSet; + +public class BlockEditAPI { + + /** + * @param selection The area that will be operated on + * @param chunksPerTick Amount of chunks per tick to modify + * @param operation Operation to execute + */ + public static void runOperation(Selection selection, int chunksPerTick, BlockEditOperation operation) { + HashSet work = WorkSplitter.getOperatedOnChunks(selection); + WorkSplitter.runOperation(work, selection, chunksPerTick, operation); + } + +} diff --git a/src/main/java/me/youhavetrouble/blockedit/api/BlockEditOperation.java b/src/main/java/me/youhavetrouble/blockedit/api/BlockEditOperation.java new file mode 100644 index 0000000..1e230fb --- /dev/null +++ b/src/main/java/me/youhavetrouble/blockedit/api/BlockEditOperation.java @@ -0,0 +1,13 @@ +package me.youhavetrouble.blockedit.api; + +import org.bukkit.block.Block; + +public interface BlockEditOperation { + + /** + * This function will run for every block in the selection it is executed on. + * @param block Current block. + */ + void transformBlock(Block block); + +} diff --git a/src/main/java/me/youhavetrouble/blockedit/commands/ReplaceCommand.java b/src/main/java/me/youhavetrouble/blockedit/commands/ReplaceCommand.java index fb511ed..491e3e5 100644 --- a/src/main/java/me/youhavetrouble/blockedit/commands/ReplaceCommand.java +++ b/src/main/java/me/youhavetrouble/blockedit/commands/ReplaceCommand.java @@ -2,7 +2,9 @@ package me.youhavetrouble.blockedit.commands; import me.youhavetrouble.blockedit.BEPlayer; import me.youhavetrouble.blockedit.WorkSplitter; +import me.youhavetrouble.blockedit.api.BlockEditAPI; import me.youhavetrouble.blockedit.operations.ReplaceOperation; +import me.youhavetrouble.blockedit.util.Selection; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.block.data.BlockData; @@ -31,12 +33,12 @@ public class ReplaceCommand implements TabExecutor { player.sendMessage(Component.text("You need to provide block type to replace")); return true; } - Material materialToReplace = Material.getMaterial(args[0].toUpperCase()); + Material materialToReplace = Material.getMaterial(args[1].toUpperCase()); if (materialToReplace == null) { player.sendMessage(Component.text("Provided material does not exist")); return true; } - Material material = Material.getMaterial(args[1].toUpperCase()); + Material material = Material.getMaterial(args[0].toUpperCase()); if (material == null) { player.sendMessage(Component.text("Provided material does not exist")); return true; @@ -50,7 +52,7 @@ public class ReplaceCommand implements TabExecutor { player.sendMessage(Component.text("You need to select 2 points to do this")); return true; } - new ReplaceOperation(WorkSplitter.getOperatedOnChunks(selection), bePlayer,blockDataToReplaceWith, blockData, 1); + BlockEditAPI.runOperation(new Selection(selection, bePlayer.getSelectionWorld()), 1, new ReplaceOperation(blockData, blockDataToReplaceWith)); return true; } diff --git a/src/main/java/me/youhavetrouble/blockedit/commands/SetCommand.java b/src/main/java/me/youhavetrouble/blockedit/commands/SetCommand.java index ad6b610..a7c12b1 100644 --- a/src/main/java/me/youhavetrouble/blockedit/commands/SetCommand.java +++ b/src/main/java/me/youhavetrouble/blockedit/commands/SetCommand.java @@ -2,7 +2,9 @@ package me.youhavetrouble.blockedit.commands; import me.youhavetrouble.blockedit.BEPlayer; import me.youhavetrouble.blockedit.WorkSplitter; +import me.youhavetrouble.blockedit.api.BlockEditAPI; import me.youhavetrouble.blockedit.operations.SetOperation; +import me.youhavetrouble.blockedit.util.Selection; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.block.data.BlockData; @@ -38,7 +40,7 @@ public class SetCommand implements TabExecutor { player.sendMessage(Component.text("You need to select 2 points to do this")); return true; } - new SetOperation(WorkSplitter.getOperatedOnChunks(selection), bePlayer, blockData, 1); + BlockEditAPI.runOperation(new Selection(selection, bePlayer.getSelectionWorld()), 1, new SetOperation(blockData)); } return true; } diff --git a/src/main/java/me/youhavetrouble/blockedit/operations/ReplaceOperation.java b/src/main/java/me/youhavetrouble/blockedit/operations/ReplaceOperation.java index a9f9a42..00414ab 100644 --- a/src/main/java/me/youhavetrouble/blockedit/operations/ReplaceOperation.java +++ b/src/main/java/me/youhavetrouble/blockedit/operations/ReplaceOperation.java @@ -1,60 +1,14 @@ package me.youhavetrouble.blockedit.operations; -import me.youhavetrouble.blockedit.BEPlayer; -import me.youhavetrouble.blockedit.BlockEdit; -import me.youhavetrouble.blockedit.util.ChunkWork; -import org.bukkit.Bukkit; -import org.bukkit.World; +import me.youhavetrouble.blockedit.api.BlockEditOperation; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; -import org.bukkit.util.BoundingBox; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; +public record ReplaceOperation(BlockData blockToReplace, BlockData blockToSet) implements BlockEditOperation { -public class ReplaceOperation { - - private final BoundingBox selection; - private final BlockData blockToSet, getBlockToReplace; - private final List chunkwork = new ArrayList<>(); - - public ReplaceOperation(HashSet chunkWorks, BEPlayer bePlayer,BlockData blockToReplace , BlockData blockToSet, int chunksPerTick) { - this.selection = bePlayer.getSelection(); - this.blockToSet = blockToSet; - this.getBlockToReplace = blockToReplace; - this.chunkwork.addAll(chunkWorks); - AtomicInteger element = new AtomicInteger(chunkwork.size()-1); - - Bukkit.getScheduler().runTaskTimer(BlockEdit.getPlugin(), (task) -> { - if (element.get() < 0) { - task.cancel(); - return; - } - for (int i = 0; i< chunksPerTick; i++) { - processChunkWork(chunkwork.get(element.getAndDecrement()), bePlayer.getSelectionWorld()); - } - }, 0, 1); + @Override + public void transformBlock(Block block) { + if (block.getBlockData().matches(blockToReplace)) + block.setBlockData(blockToSet); } - - private void processChunkWork(ChunkWork chunkWork, UUID worldUuid) { - World world = Bukkit.getWorld(worldUuid); - if (world == null) return; - chunkWork.getChunkAsync(world).thenAccept(chunk -> { - // skip y levels that are not in the selection - for (int y = (int) selection.getMinY(); y <= selection.getMaxY(); y++) { - for (int x = 0; x <= 15; x++) { - for (int z = 0; z <= 15; z++) { - Block block = chunk.getBlock(x, y, z); - if (!selection.contains(block.getLocation().toVector())) continue; - if (block.getBlockData().matches(getBlockToReplace)) - block.setBlockData(blockToSet); - } - } - } - }); - } - } diff --git a/src/main/java/me/youhavetrouble/blockedit/operations/SetOperation.java b/src/main/java/me/youhavetrouble/blockedit/operations/SetOperation.java index 23ea42d..a85e617 100644 --- a/src/main/java/me/youhavetrouble/blockedit/operations/SetOperation.java +++ b/src/main/java/me/youhavetrouble/blockedit/operations/SetOperation.java @@ -1,58 +1,15 @@ package me.youhavetrouble.blockedit.operations; -import me.youhavetrouble.blockedit.BEPlayer; -import me.youhavetrouble.blockedit.BlockEdit; -import me.youhavetrouble.blockedit.util.ChunkWork; -import org.bukkit.Bukkit; -import org.bukkit.World; + +import me.youhavetrouble.blockedit.api.BlockEditOperation; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; -import org.bukkit.util.BoundingBox; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -public class SetOperation { - private final BoundingBox selection; - private final BlockData blockToSet; - private final List chunkwork = new ArrayList<>(); +public record SetOperation(BlockData blockData) implements BlockEditOperation { - public SetOperation(HashSet chunkWorks, BEPlayer bePlayer, BlockData blockToSet, int chunksPerTick) { - this.selection = bePlayer.getSelection(); - this.blockToSet = blockToSet; - this.chunkwork.addAll(chunkWorks); - - AtomicInteger element = new AtomicInteger(chunkwork.size()-1); - - Bukkit.getScheduler().runTaskTimer(BlockEdit.getPlugin(), (task) -> { - if (element.get() < 0) { - task.cancel(); - return; - } - for (int i = 0; i< chunksPerTick; i++) { - processChunkWork(chunkwork.get(element.getAndDecrement()), bePlayer.getSelectionWorld()); - } - }, 0, 1); + @Override + public void transformBlock(Block block) { + block.setBlockData(blockData); } - - private void processChunkWork(ChunkWork chunkWork, UUID worldUuid) { - World world = Bukkit.getWorld(worldUuid); - if (world == null) return; - chunkWork.getChunkAsync(world).thenAccept(chunk -> { - // skip y levels that are not in the selection - for (int y = (int) selection.getMinY(); y <= selection.getMaxY(); y++) { - for (int x = 0; x <= 15; x++) { - for (int z = 0; z <= 15; z++) { - Block block = chunk.getBlock(x, y, z); - if (!selection.contains(block.getLocation().toVector())) continue; - block.setBlockData(blockToSet); - } - } - } - }); - } - } diff --git a/src/main/java/me/youhavetrouble/blockedit/util/Selection.java b/src/main/java/me/youhavetrouble/blockedit/util/Selection.java new file mode 100644 index 0000000..e731895 --- /dev/null +++ b/src/main/java/me/youhavetrouble/blockedit/util/Selection.java @@ -0,0 +1,35 @@ +package me.youhavetrouble.blockedit.util; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.util.BoundingBox; + +import java.util.UUID; + +public class Selection extends BoundingBox { + + private UUID worldUuid; + + public Selection(Location location1, Location location2, UUID worldUuid) { + super(location1.getX(), location1.getY(), location1.getZ(), location2.getX(), location2.getY(), location2.getZ()); + this.worldUuid = worldUuid; + } + + public Selection(BoundingBox boundingBox, UUID worldUuid) { + super(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ()); + this.worldUuid = worldUuid; + } + + public World getWorld() { + return Bukkit.getWorld(worldUuid); + } + + public UUID getWorldUuid() { + return worldUuid; + } + + public void setWorldUuid(UUID worldUuid) { + this.worldUuid = worldUuid; + } +} diff --git a/src/test/java/WorkSplitterTest.java b/src/test/java/WorkSplitterTest.java index f825ab6..ff80b10 100644 --- a/src/test/java/WorkSplitterTest.java +++ b/src/test/java/WorkSplitterTest.java @@ -7,22 +7,4 @@ import static org.junit.Assert.assertEquals; public class WorkSplitterTest { - /** - * Test if location -> chunk location works properly - */ - @Test - public void testLocationToChunk() { - ChunkWork testOne = new ChunkWork(0,0); - ChunkWork workSplitterTestOne = WorkSplitter.locationToChunkWork(15, 15); - - ChunkWork testTwo = WorkSplitter.locationToChunkWork(10.233D, -138.788D); - ChunkWork workSplitterTestTwo = new ChunkWork(0,-9); - - assertEquals(workSplitterTestOne.getX(), testOne.getX()); - assertEquals(workSplitterTestOne.getZ(), testOne.getZ()); - - assertEquals(workSplitterTestTwo.getX(), testTwo.getX()); - assertEquals(workSplitterTestTwo.getZ(), testTwo.getZ()); - } - }