mirror of
https://github.com/YouHaveTrouble/BlockEdit.git
synced 2026-06-29 13:36:19 +00:00
refactor in a way to provide possibility of querying progress of running operation
This commit is contained in:
@@ -7,7 +7,7 @@ import me.youhavetrouble.blockedit.util.ChunkWork;
|
|||||||
import me.youhavetrouble.blockedit.util.Selection;
|
import me.youhavetrouble.blockedit.util.Selection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.Set;
|
||||||
|
|
||||||
public class BlockEditAPI {
|
public class BlockEditAPI {
|
||||||
|
|
||||||
@@ -17,13 +17,13 @@ public class BlockEditAPI {
|
|||||||
* @param chunksPerTick Amount of chunks per tick to modify
|
* @param chunksPerTick Amount of chunks per tick to modify
|
||||||
* @param operation Operation to execute
|
* @param operation Operation to execute
|
||||||
*/
|
*/
|
||||||
public static void runOperation(
|
public static OperationWork runOperation(
|
||||||
@NotNull Selection selection,
|
@NotNull Selection selection,
|
||||||
int chunksPerTick,
|
int chunksPerTick,
|
||||||
@NotNull BlockEditOperation operation
|
@NotNull BlockEditOperation operation
|
||||||
) {
|
) {
|
||||||
HashSet<ChunkWork> work = WorkSplitter.getOperatedOnChunks(selection);
|
Set<ChunkWork> work = WorkSplitter.getOperatedOnChunks(selection);
|
||||||
WorkSplitter.runOperation(work, selection, chunksPerTick, operation);
|
return WorkSplitter.runOperation(work, selection, chunksPerTick, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package me.youhavetrouble.blockedit.api;
|
||||||
|
|
||||||
|
import me.youhavetrouble.blockedit.BlockEdit;
|
||||||
|
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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class OperationWork {
|
||||||
|
|
||||||
|
private boolean started, finished = false;
|
||||||
|
private final List<ChunkWork> chunkWorkList = new ArrayList<>();
|
||||||
|
private final Selection selection;
|
||||||
|
private final int chunksPerTick;
|
||||||
|
private final BlockEditOperation operation;
|
||||||
|
private final AtomicInteger chunksLeft;
|
||||||
|
private final AtomicInteger chunksProcessed = new AtomicInteger(0);
|
||||||
|
|
||||||
|
protected OperationWork(
|
||||||
|
Set<ChunkWork> chunkWorks,
|
||||||
|
Selection selection,
|
||||||
|
int chunksPerTick,
|
||||||
|
BlockEditOperation operation
|
||||||
|
) {
|
||||||
|
this.chunkWorkList.addAll(chunkWorks);
|
||||||
|
this.chunksLeft = new AtomicInteger(this.chunkWorkList.size() - 1);
|
||||||
|
this.selection = new Selection(selection.clone().expand(0.1), selection.getWorldUuid());
|
||||||
|
this.chunksPerTick = chunksPerTick;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the operation. This will start processing chunks in the background.
|
||||||
|
* This method can only be called once.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if called when the operation has already been started
|
||||||
|
*/
|
||||||
|
protected void start() {
|
||||||
|
if (started) throw new IllegalStateException("Operation already started");
|
||||||
|
started = true;
|
||||||
|
Bukkit.getGlobalRegionScheduler().runAtFixedRate(BlockEdit.getPlugin(), (task -> {
|
||||||
|
if (chunksLeft.get() < 0) {
|
||||||
|
this.finished = true;
|
||||||
|
task.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < chunksPerTick; i++) {
|
||||||
|
int chunkWorkIndex = chunksLeft.getAndDecrement();
|
||||||
|
ChunkWork chunkWork = chunkWorkList.get(chunkWorkIndex);
|
||||||
|
World world = selection.getWorld();
|
||||||
|
if (world == null) return;
|
||||||
|
Bukkit.getRegionScheduler().execute(BlockEdit.getPlugin(), selection.getWorld(), chunkWork.getX(), chunkWork.getZ(), () -> {
|
||||||
|
processChunkWork(chunkWork, selection, operation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}), 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total amount of chunks that will be processed.
|
||||||
|
*
|
||||||
|
* @return Total amount of chunks
|
||||||
|
*/
|
||||||
|
public int getTotalChunks() {
|
||||||
|
return chunkWorkList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of chunks processed. This might not match total amount of chunks after operation finishes.
|
||||||
|
*
|
||||||
|
* @return Amount of chunks processed
|
||||||
|
* @see #finished to check if the operation has finished
|
||||||
|
*/
|
||||||
|
public int getChunksProcessed() {
|
||||||
|
return chunksProcessed.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of chunks left to process.
|
||||||
|
*
|
||||||
|
* @return Amount of chunks left to process
|
||||||
|
*/
|
||||||
|
public int getChunksLeft() {
|
||||||
|
return chunksLeft.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the operation has finished. This will return true after all work tasks have been delegated.
|
||||||
|
*
|
||||||
|
* @return Whether the operation has finished
|
||||||
|
* @see #getChunksLeft() to get the amount of chunks left to process
|
||||||
|
* @see #getChunksProcessed() to get the amount of chunks processed
|
||||||
|
*/
|
||||||
|
public boolean isFinished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).thenRunAsync(this.chunksProcessed::incrementAndGet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,21 +1,16 @@
|
|||||||
package me.youhavetrouble.blockedit.api;
|
package me.youhavetrouble.blockedit.api;
|
||||||
|
|
||||||
import me.youhavetrouble.blockedit.BlockEdit;
|
|
||||||
import me.youhavetrouble.blockedit.util.ChunkWork;
|
import me.youhavetrouble.blockedit.util.ChunkWork;
|
||||||
import me.youhavetrouble.blockedit.util.Selection;
|
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 org.bukkit.util.BoundingBox;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public class WorkSplitter {
|
public class WorkSplitter {
|
||||||
|
|
||||||
protected static HashSet<ChunkWork> getOperatedOnChunks(BoundingBox boundingBox) {
|
protected static Set<ChunkWork> getOperatedOnChunks(BoundingBox boundingBox) {
|
||||||
HashSet<ChunkWork> chunks = new HashSet<>();
|
HashSet<ChunkWork> chunks = new HashSet<>();
|
||||||
int minChunkX = (int) Math.floor(boundingBox.getMinX()) >> 4;
|
int minChunkX = (int) Math.floor(boundingBox.getMinX()) >> 4;
|
||||||
int maxChunkX = (int) Math.floor(boundingBox.getMaxX()) >> 4;
|
int maxChunkX = (int) Math.floor(boundingBox.getMaxX()) >> 4;
|
||||||
@@ -32,42 +27,10 @@ public class WorkSplitter {
|
|||||||
return chunks;
|
return chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void runOperation(HashSet<ChunkWork> chunkWorks, Selection selection, int chunksPerTick, BlockEditOperation operation) {
|
protected static OperationWork runOperation(@NotNull Set<ChunkWork> chunkWorks, @NotNull Selection selection, int chunksPerTick, @NotNull BlockEditOperation operation) {
|
||||||
if (selection == null) return;
|
OperationWork operationWork = new OperationWork(chunkWorks, selection, chunksPerTick, operation);
|
||||||
Selection sel = new Selection(selection.clone().expand(0.1), selection.getWorldUuid());
|
operationWork.start();
|
||||||
List<ChunkWork> chunkWorkList = new ArrayList<>(chunkWorks);
|
return operationWork;
|
||||||
AtomicInteger element = new AtomicInteger(chunkWorkList.size()-1);
|
|
||||||
Bukkit.getGlobalRegionScheduler().runAtFixedRate(BlockEdit.getPlugin(), (task -> {
|
|
||||||
if (element.get() < 0) {
|
|
||||||
task.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i< chunksPerTick; i++) {
|
|
||||||
int chunkWorkIndex = element.getAndDecrement();
|
|
||||||
ChunkWork chunkWork = chunkWorkList.get(chunkWorkIndex);
|
|
||||||
Bukkit.getRegionScheduler().execute(BlockEdit.getPlugin(), selection.getWorld(), chunkWork.getX(), chunkWork.getZ(), () -> {
|
|
||||||
processChunkWork(chunkWork, sel, operation);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}), 1, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.bukkit.World;
|
|||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -27,18 +28,22 @@ public class Selection extends BoundingBox {
|
|||||||
this.worldUuid = worldUuid;
|
this.worldUuid = worldUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Location getSelectionPoint1() {
|
public Location getSelectionPoint1() {
|
||||||
return selectionPoint1;
|
return selectionPoint1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Location getSelectionPoint2() {
|
public Location getSelectionPoint2() {
|
||||||
return selectionPoint2;
|
return selectionPoint2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public World getWorld() {
|
public World getWorld() {
|
||||||
return Bukkit.getWorld(worldUuid);
|
return Bukkit.getWorld(worldUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
public UUID getWorldUuid() {
|
public UUID getWorldUuid() {
|
||||||
return worldUuid;
|
return worldUuid;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user