diff --git a/common/src/main/java/org/popcraft/chunky/Chunky.java b/common/src/main/java/org/popcraft/chunky/Chunky.java index d815bca2..11ecab0e 100644 --- a/common/src/main/java/org/popcraft/chunky/Chunky.java +++ b/common/src/main/java/org/popcraft/chunky/Chunky.java @@ -54,6 +54,7 @@ public class Chunky { private final Selection.Builder selection; private final TaskScheduler scheduler = new TaskScheduler(); private final Map generationTasks = new ConcurrentHashMap<>(); + private final Map trimTasks = new ConcurrentHashMap<>(); private final Map pendingActions = new HashMap<>(); private final RegionCache regionCache = new RegionCache(); private final double limit; @@ -151,6 +152,10 @@ public Map getGenerationTasks() { return generationTasks; } + public Map getTrimTasks() { + return trimTasks; + } + public Map getCommands() { return commands; } diff --git a/common/src/main/java/org/popcraft/chunky/command/CancelCommand.java b/common/src/main/java/org/popcraft/chunky/command/CancelCommand.java index b24bf95b..dfe00944 100644 --- a/common/src/main/java/org/popcraft/chunky/command/CancelCommand.java +++ b/common/src/main/java/org/popcraft/chunky/command/CancelCommand.java @@ -22,7 +22,10 @@ public CancelCommand(final Chunky chunky) { @Override public void execute(final Sender sender, final CommandArguments arguments) { final Map generationTasks = chunky.getGenerationTasks(); - if (generationTasks.isEmpty() && chunky.getTaskLoader().loadTasks().stream().allMatch(GenerationTask::isCancelled)) { + final Map trimTasks = chunky.getTrimTasks(); + if (generationTasks.isEmpty() + && chunky.getTaskLoader().loadTasks().stream().allMatch(GenerationTask::isCancelled) + && trimTasks.isEmpty()) { sender.sendMessagePrefixed(TranslationKey.FORMAT_CANCEL_NO_TASKS); return; } @@ -39,6 +42,9 @@ public void execute(final Sender sender, final CommandArguments arguments) { if (chunky.getGenerationTasks().containsKey(world.get().getName())) { chunky.getGenerationTasks().remove(world.get().getName()).stop(true); } + if (chunky.getTrimTasks().containsKey(world.get().getName())) { + chunky.getTrimTasks().remove(world.get().getName()).setCancelled(true); + } }; } else { cancelAction = () -> { @@ -47,6 +53,8 @@ public void execute(final Sender sender, final CommandArguments arguments) { chunky.getGenerationTasks().values().forEach(generationTask -> generationTask.stop(true)); chunky.getGenerationTasks().clear(); chunky.getScheduler().cancelTasks(); + chunky.getTrimTasks().values().forEach(trimTask -> trimTask.setCancelled(true)); + chunky.getTrimTasks().clear(); }; } chunky.setPendingAction(sender, cancelAction); diff --git a/common/src/main/java/org/popcraft/chunky/command/TrimCommand.java b/common/src/main/java/org/popcraft/chunky/command/TrimCommand.java index 6fdbeb21..72cbeb5c 100644 --- a/common/src/main/java/org/popcraft/chunky/command/TrimCommand.java +++ b/common/src/main/java/org/popcraft/chunky/command/TrimCommand.java @@ -89,19 +89,42 @@ public void execute(final Sender sender, final CommandArguments arguments) { final Shape shape = ShapeFactory.getShape(selection); final Runnable deletionAction = () -> chunky.getScheduler().runTask(() -> { sender.sendMessagePrefixed(TranslationKey.FORMAT_START, selection.world().getName(), translate("shape_" + selection.shape()), Formatting.number(selection.centerX()), Formatting.number(selection.centerZ()), Formatting.radius(selection)); + TrimCommand.Task trimTask = new TrimCommand.Task(); + chunky.getTrimTasks().put(selection.world().getName(), trimTask); final Optional regionPath = selection.world().getRegionDirectory(); + final AtomicLong finishedRegions = new AtomicLong(); final AtomicLong deleted = new AtomicLong(); final long startTime = System.currentTimeMillis(); + final AtomicLong updateTime = new AtomicLong(startTime); try { if (regionPath.isPresent()) { - try (final Stream regionWalker = Files.walk(regionPath.get())) { - regionWalker.forEach(region -> deleted.getAndAdd(checkRegion(selection.world(), region.getFileName().toString(), shape, inside, inhabitedTimeCheck, inhabitedTime))); + try (final Stream files = Files.list(regionPath.get())) { + final List regions = files + .filter(file -> tryRegionCoordinate(file.getFileName().toString()).isPresent()) + .toList(); + final long totalRegions = regions.size(); + for (final Path region: regions) { + if (trimTask.isCancelled()) { + break; + } + deleted.getAndAdd(checkRegion(selection.world(), region.getFileName().toString(), shape, inside, inhabitedTimeCheck, inhabitedTime)); + finishedRegions.getAndIncrement(); + if (!trimTask.isCancelled() && !chunky.getConfig().isSilent()) { + final long currentTime = System.currentTimeMillis(); + final boolean updateIntervalElapsed = ((currentTime - updateTime.get()) / 1e3) > chunky.getConfig().getUpdateInterval(); + if (updateIntervalElapsed) { + sender.sendMessagePrefixed(TranslationKey.TASK_TRIM_UPDATE, selection.world().getName(), finishedRegions.get(), String.format("%.2f", 100f * finishedRegions.get() / totalRegions)); + updateTime.set(currentTime); + } + } + } } } } catch (IOException e) { e.printStackTrace(); } final long totalTime = System.currentTimeMillis() - startTime; + chunky.getTrimTasks().remove(selection.world().getName()); sender.sendMessagePrefixed(TranslationKey.TASK_TRIM, deleted.get(), selection.world().getName(), String.format("%.3f", totalTime / 1e3f)); }); chunky.setPendingAction(sender, deletionAction); @@ -261,4 +284,16 @@ public List suggestions(final CommandArguments arguments) { } return List.of(); } + + public static final class Task { + private boolean cancelled; + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + } } diff --git a/common/src/main/java/org/popcraft/chunky/util/TranslationKey.java b/common/src/main/java/org/popcraft/chunky/util/TranslationKey.java index c658b6b2..23fc5894 100644 --- a/common/src/main/java/org/popcraft/chunky/util/TranslationKey.java +++ b/common/src/main/java/org/popcraft/chunky/util/TranslationKey.java @@ -88,6 +88,7 @@ public final class TranslationKey { public static final String SHAPE_STAR = "shape_star"; public static final String SHAPE_TRIANGLE = "shape_triangle"; public static final String TASK_TRIM = "task_trim"; + public static final String TASK_TRIM_UPDATE = "task_trim_update"; public static final String TASK_DONE = "task_done"; public static final String TASK_STOPPED = "task_stopped"; public static final String TASK_UPDATE = "task_update"; diff --git a/common/src/main/resources/lang/en.json b/common/src/main/resources/lang/en.json index b6973101..989da2fb 100644 --- a/common/src/main/resources/lang/en.json +++ b/common/src/main/resources/lang/en.json @@ -86,6 +86,7 @@ "shape_star": "star", "shape_triangle": "triangle", "task_trim": "Deleted %s chunks from %s in %s seconds.", + "task_trim_update": "Task running for %s. Processed: %s regions (%s%%)", "task_done": "Task finished for %s. Processed: %s chunks (%s%%), Total time: %s:%s:%s", "task_stopped": "Task stopped for %s.", "task_update": "Task running for %s. Processed: %s chunks (%s%%), ETA: %s:%s:%s, Rate: %s cps, Current: %s, %s",