diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/inject/BukkitModule.java b/Bukkit/src/main/java/com/plotsquared/bukkit/inject/BukkitModule.java index e9fb0d92f..02c3e7176 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/inject/BukkitModule.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/inject/BukkitModule.java @@ -29,6 +29,7 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.assistedinject.FactoryModuleBuilder; +import com.google.inject.assistedinject.FactoryProvider; import com.plotsquared.bukkit.BukkitPlatform; import com.plotsquared.bukkit.player.BukkitPlayerManager; import com.plotsquared.bukkit.queue.BukkitChunkCoordinator; @@ -49,12 +50,15 @@ import com.plotsquared.core.inject.annotations.DefaultGenerator; import com.plotsquared.core.inject.factory.ChunkCoordinatorBuilderFactory; import com.plotsquared.core.inject.factory.ChunkCoordinatorFactory; import com.plotsquared.core.inject.factory.HybridPlotWorldFactory; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.plot.world.DefaultPlotAreaManager; import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.plot.world.SinglePlotAreaManager; import com.plotsquared.core.queue.ChunkCoordinator; import com.plotsquared.core.queue.GlobalBlockQueue; import com.plotsquared.core.queue.QueueProvider; +import com.plotsquared.core.queue.subscriber.DefaultProgressSubscriber; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.plotsquared.core.util.ChunkManager; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.InventoryUtil; @@ -93,6 +97,7 @@ public class BukkitModule extends AbstractModule { bind(InventoryUtil.class).to(BukkitInventoryUtil.class); bind(SetupUtils.class).to(BukkitSetupUtils.class); bind(WorldUtil.class).to(BukkitUtil.class); + install(new FactoryModuleBuilder().implement(ProgressSubscriber.class, DefaultProgressSubscriber.class).build(ProgressSubscriberFactory.class)); bind(GlobalBlockQueue.class).toInstance(new GlobalBlockQueue(QueueProvider.of(BukkitQueueCoordinator.class))); bind(ChunkManager.class).to(BukkitChunkManager.class); bind(RegionManager.class).to(BukkitRegionManager.class); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitChunkCoordinator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitChunkCoordinator.java index 8d50f7a11..53e45d3aa 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitChunkCoordinator.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitChunkCoordinator.java @@ -29,6 +29,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.plotsquared.bukkit.BukkitPlatform; import com.plotsquared.core.queue.ChunkCoordinator; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.plotsquared.core.util.task.TaskManager; import com.plotsquared.core.util.task.TaskTime; import com.sk89q.worldedit.math.BlockVector2; @@ -82,7 +83,8 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { @Assisted @Nonnull final Collection requestedChunks, @Assisted @Nonnull final Runnable whenDone, @Assisted @Nonnull final Consumer throwableConsumer, - @Assisted final boolean unloadAfter) { + @Assisted final boolean unloadAfter, + @Assisted @Nonnull final Collection progressSubscribers) { this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks); this.availableChunks = new LinkedBlockingQueue<>(); this.totalSize = requestedChunks.size(); @@ -95,6 +97,7 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { this.unloadAfter = unloadAfter; this.plugin = JavaPlugin.getPlugin(BukkitPlatform.class); this.bukkitWorld = Bukkit.getWorld(world.getName()); + this.progressSubscribers.addAll(progressSubscribers); } @Override public void start() { @@ -109,7 +112,7 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { if (chunk == null) { return; } - long iterationTime; + long[] iterationTime = new long[2]; int processedChunks = 0; do { final long start = System.currentTimeMillis(); @@ -124,8 +127,9 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { processedChunks++; final long end = System.currentTimeMillis(); // Update iteration time - iterationTime = end - start; - } while (2 * iterationTime /* last chunk + next chunk */ < this.maxIterationTime && (chunk = availableChunks.poll()) != null); + iterationTime[0] = iterationTime[1]; + iterationTime[1] = end - start; + } while (iterationTime[0] + iterationTime[1] < this.maxIterationTime * 2 && (chunk = availableChunks.poll()) != null); if (processedChunks < this.batchSize) { // Adjust batch size based on the amount of processed chunks per tick this.batchSize = processedChunks; @@ -133,20 +137,23 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { final int expected = this.expectedSize.addAndGet(-processedChunks); - final float progress = ((float) totalSize - (float) expected) / (float) totalSize; - for (final ProgressSubscriber subscriber : this.progressSubscribers) { - subscriber.notifyProgress(this, progress); - } - if (expected <= 0) { try { this.whenDone.run(); } catch (final Throwable throwable) { this.throwableConsumer.accept(throwable); + } finally { + for (final ProgressSubscriber subscriber : this.progressSubscribers) { + subscriber.notifyEnd(); + } + this.cancel(); } - this.cancel(); } else { if (this.availableChunks.size() < processedChunks) { + final double progress = ((double) totalSize - (double) expected) / (double) totalSize; + for (final ProgressSubscriber subscriber : this.progressSubscribers) { + subscriber.notifyProgress(this, progress); + } this.requestBatch(); } } @@ -205,21 +212,8 @@ public final class BukkitChunkCoordinator extends ChunkCoordinator { * * @param subscriber Subscriber */ - public void subscribeToProgress(@Nonnull final BukkitChunkCoordinator.ProgressSubscriber subscriber) { + public void subscribeToProgress(@Nonnull final ProgressSubscriber subscriber) { this.progressSubscribers.add(subscriber); } - @FunctionalInterface - public interface ProgressSubscriber { - - /** - * Notify about a progress update in the coordinator - * - * @param coordinator Coordinator instance that triggered the notification - * @param progress Progress in the range [0, 1] - */ - void notifyProgress(@Nonnull final BukkitChunkCoordinator coordinator, final float progress); - - } - } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java index a0e5b028f..e9b463a9b 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitQueueCoordinator.java @@ -63,7 +63,8 @@ import java.util.function.Consumer; public class BukkitQueueCoordinator extends BasicQueueCoordinator { - private final SideEffectSet sideEffectSet; + private final SideEffectSet noSideEffectSet; + private final SideEffectSet lightingSideEffectSet; private org.bukkit.World bukkitWorld; @Inject private ChunkCoordinatorBuilderFactory chunkCoordinatorBuilderFactory; @Inject private ChunkCoordinatorFactory chunkCoordinatorFactory; @@ -71,7 +72,8 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator { @Inject public BukkitQueueCoordinator(@Nonnull World world) { super(world); - sideEffectSet = SideEffectSet.none().with(SideEffect.LIGHTING, SideEffect.State.OFF).with(SideEffect.NEIGHBORS, SideEffect.State.OFF); + noSideEffectSet = SideEffectSet.none().with(SideEffect.LIGHTING, SideEffect.State.OFF).with(SideEffect.NEIGHBORS, SideEffect.State.OFF); + lightingSideEffectSet = SideEffectSet.none().with(SideEffect.NEIGHBORS, SideEffect.State.OFF); } @Override public BlockState getBlock(int x, int y, int z) { @@ -177,7 +179,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator { localChunk.getTiles().forEach(((blockVector3, tag) -> { try { BaseBlock block = getWorld().getBlock(blockVector3).toBaseBlock(tag); - getWorld().setBlock(blockVector3, block, sideEffectSet); + getWorld().setBlock(blockVector3, block, noSideEffectSet); } catch (WorldEditException ignored) { StateWrapper sw = new StateWrapper(tag); sw.restoreTag(getWorld().getName(), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()); @@ -185,9 +187,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator { })); } if (localChunk.getEntities().size() > 0) { - localChunk.getEntities().forEach((location, entity) -> { - getWorld().createEntity(location, entity); - }); + localChunk.getEntities().forEach((location, entity) -> getWorld().createEntity(location, entity)); } }; } @@ -198,7 +198,7 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator { chunkCoordinator = chunkCoordinatorBuilderFactory.create(chunkCoordinatorFactory).inWorld(getWorld()).withChunks(getBlockChunks().keySet()).withChunks(read) .withInitialBatchSize(3).withMaxIterationTime(40).withThrowableConsumer(Throwable::printStackTrace).withFinalAction(getCompleteTask()) - .withConsumer(consumer).unloadAfter(isUnloadAfter()).build(); + .withConsumer(consumer).unloadAfter(isUnloadAfter()).withProgressSubscribers(getProgressSubscribers()).build(); return super.enqueue(); } @@ -207,7 +207,23 @@ public class BukkitQueueCoordinator extends BasicQueueCoordinator { */ private void setWorldBlock(int x, int y, int z, @Nonnull BaseBlock block, @Nonnull BlockVector2 blockVector2) { try { - getWorld().setBlock(BlockVector3.at(x, y, z), block, sideEffectSet); + BlockVector3 loc = BlockVector3.at(x, y, z); + boolean lighting = false; + switch (getLightingMode()) { + case NONE: + break; + case PLACEMENT: + lighting = block.getBlockType().getMaterial().getLightValue() > 0; + break; + case REPLACEMENT: + lighting = block.getBlockType().getMaterial().getLightValue() > 0 + || getWorld().getBlock(loc).getBlockType().getMaterial().getLightValue() > 0; + break; + default: + // Can only be "all" + lighting = true; + } + getWorld().setBlock(loc, block, lighting ? lightingSideEffectSet : noSideEffectSet); } catch (WorldEditException ignored) { // Fallback to not so nice method BlockData blockData = BukkitAdapter.adapt(block); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/ChunkCoordinator.java b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/ChunkCoordinator.java deleted file mode 100644 index 97d2f6eab..000000000 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/ChunkCoordinator.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * _____ _ _ _____ _ - * | __ \| | | | / ____| | | - * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | - * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | - * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | - * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| - * | | - * |_| - * PlotSquared plot management system for Minecraft - * Copyright (C) 2020 IntellectualSites - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.plotsquared.bukkit.queue; - -import com.google.common.base.Preconditions; -import com.plotsquared.bukkit.BukkitPlatform; -import com.sk89q.worldedit.math.BlockVector2; -import io.papermc.lib.PaperLib; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitRunnable; - -import javax.annotation.Nonnull; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -/** - * Utility that allows for the loading and coordination of chunk actions - *

- * The coordinator takes in collection of chunk coordinates, loads them - * and allows the caller to specify a sink for the loaded chunks. The - * coordinator will prevent the chunks from being unloaded until the sink - * has fully consumed the chunk - *

- * Usage: - *

{@code
- * final ChunkCoordinator chunkCoordinator = ChunkCoordinator.builder()
- *     .inWorld(Objects.requireNonNull(Bukkit.getWorld("world"))).withChunk(BlockVector2.at(0, 0))
- *     .withConsumer(chunk -> System.out.printf("Got chunk %d;%d", chunk.getX(), chunk.getZ()))
- *     .withFinalAction(() -> System.out.println("All chunks have been loaded"))
- *     .withThrowableConsumer(throwable -> System.err.println("Something went wrong... =("))
- *     .withMaxIterationTime(25L)
- *     .build();
- * chunkCoordinator.subscribeToProgress((coordinator, progress) ->
- *     System.out.printf("Progress: %.1f", progress * 100.0f));
- * chunkCoordinator.start();
- * }
- * - * @author Alexander Söderberg - * @see #builder() To create a new coordinator instance - */ -public final class ChunkCoordinator extends BukkitRunnable { - - private final List progressSubscribers = new LinkedList<>(); - - private final Queue requestedChunks; - private final Queue availableChunks; - private final long maxIterationTime; - private final Plugin plugin; - private final Consumer chunkConsumer; - private final World world; - private final Runnable whenDone; - private final Consumer throwableConsumer; - private final int totalSize; - - private AtomicInteger expectedSize; - private int batchSize; - - private ChunkCoordinator(final long maxIterationTime, final int initialBatchSize, - @Nonnull final Consumer chunkConsumer, @Nonnull final World world, - @Nonnull final Collection requestedChunks, @Nonnull final Runnable whenDone, - @Nonnull final Consumer throwableConsumer) { - this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks); - this.availableChunks = new LinkedBlockingQueue<>(); - this.totalSize = requestedChunks.size(); - this.expectedSize = new AtomicInteger(this.totalSize); - this.world = world; - this.batchSize = initialBatchSize; - this.chunkConsumer = chunkConsumer; - this.maxIterationTime = maxIterationTime; - this.whenDone = whenDone; - this.throwableConsumer = throwableConsumer; - this.plugin = JavaPlugin.getPlugin(BukkitPlatform.class); - } - - /** - * Create a new {@link ChunkCoordinator} instance - * - * @return Coordinator builder instance - */ - @Nonnull public static ChunkCoordinatorBuilder builder() { - return new ChunkCoordinatorBuilder(); - } - - /** - * Start the coordinator instance - */ - public void start() { - // Request initial batch - this.requestBatch(); - // Wait until next tick to give the chunks a chance to be loaded - this.runTaskTimer(this.plugin, 1L, 1L); - } - - @Override public void run() { - Chunk chunk = this.availableChunks.poll(); - if (chunk == null) { - return; - } - long iterationTime; - int processedChunks = 0; - do { - final long start = System.currentTimeMillis(); - try { - this.chunkConsumer.accept(chunk); - } catch (final Throwable throwable) { - this.throwableConsumer.accept(throwable); - } - this.freeChunk(chunk); - processedChunks++; - final long end = System.currentTimeMillis(); - // Update iteration time - iterationTime = end - start; - } while (2 * iterationTime /* last chunk + next chunk */ < this.maxIterationTime - && (chunk = availableChunks.poll()) != null); - if (processedChunks < this.batchSize) { - // Adjust batch size based on the amount of processed chunks per tick - this.batchSize = processedChunks; - } - - final int expected = this.expectedSize.addAndGet(-processedChunks); - - final float progress = ((float) totalSize - (float) expected) / (float) totalSize; - for (final ProgressSubscriber subscriber : this.progressSubscribers) { - subscriber.notifyProgress(this, progress); - } - - if (expected <= 0) { - try { - this.whenDone.run(); - } catch (final Throwable throwable) { - this.throwableConsumer.accept(throwable); - } - this.cancel(); - } else { - if (this.availableChunks.size() < processedChunks) { - this.requestBatch(); - } - } - } - - private void requestBatch() { - BlockVector2 chunk; - for (int i = 0; i < this.batchSize && (chunk = this.requestedChunks.poll()) != null; i++) { - // This required PaperLib to be bumped to version 1.0.4 to mark the request as urgent - PaperLib.getChunkAtAsync(this.world, chunk.getX(), chunk.getZ(), true, true) - .whenComplete((chunkObject, throwable) -> { - if (throwable != null) { - throwable.printStackTrace(); - // We want one less because this couldn't be processed - this.expectedSize.decrementAndGet(); - } else { - this.processChunk(chunkObject); - } - }); - } - } - - private void processChunk(@Nonnull final Chunk chunk) { - if (!chunk.isLoaded()) { - throw new IllegalArgumentException( - String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ())); - } - chunk.addPluginChunkTicket(this.plugin); - this.availableChunks.add(chunk); - } - - private void freeChunk(@Nonnull final Chunk chunk) { - if (!chunk.isLoaded()) { - throw new IllegalArgumentException( - String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ())); - } - chunk.removePluginChunkTicket(this.plugin); - } - - /** - * Get the amount of remaining chunks (at the time of the method call) - * - * @return Snapshot view of remaining chunk count - */ - public int getRemainingChunks() { - return this.expectedSize.get(); - } - - /** - * Get the amount of requested chunks - * - * @return Requested chunk count - */ - public int getTotalChunks() { - return this.totalSize; - } - - /** - * Subscribe to coordinator progress updates - * - * @param subscriber Subscriber - */ - public void subscribeToProgress(@Nonnull final ChunkCoordinator.ProgressSubscriber subscriber) { - this.progressSubscribers.add(subscriber); - } - - - @FunctionalInterface - public interface ProgressSubscriber { - - /** - * Notify about a progress update in the coordinator - * - * @param coordinator Coordinator instance that triggered the notification - * @param progress Progress in the range [0, 1] - */ - void notifyProgress(@Nonnull final ChunkCoordinator coordinator, final float progress); - - } - - - public static final class ChunkCoordinatorBuilder { - - private final List requestedChunks = new LinkedList<>(); - private Consumer throwableConsumer = Throwable::printStackTrace; - private World world; - private Consumer chunkConsumer; - private Runnable whenDone = () -> { - }; - private long maxIterationTime = 60; // A little over 1 tick; - private int initialBatchSize = 4; - - private ChunkCoordinatorBuilder() { - } - - @Nonnull public ChunkCoordinatorBuilder inWorld(@Nonnull final World world) { - this.world = Preconditions.checkNotNull(world, "World may not be null"); - return this; - } - - @Nonnull - public ChunkCoordinatorBuilder withChunk(@Nonnull final BlockVector2 chunkLocation) { - this.requestedChunks - .add(Preconditions.checkNotNull(chunkLocation, "Chunk location may not be null")); - return this; - } - - @Nonnull public ChunkCoordinatorBuilder withChunks( - @Nonnull final Collection chunkLocations) { - chunkLocations.forEach(this::withChunk); - return this; - } - - @Nonnull - public ChunkCoordinatorBuilder withConsumer(@Nonnull final Consumer chunkConsumer) { - this.chunkConsumer = - Preconditions.checkNotNull(chunkConsumer, "Chunk consumer may not be null"); - return this; - } - - @Nonnull public ChunkCoordinatorBuilder withFinalAction(@Nonnull final Runnable whenDone) { - this.whenDone = Preconditions.checkNotNull(whenDone, "Final action may not be null"); - return this; - } - - @Nonnull public ChunkCoordinatorBuilder withMaxIterationTime(final long maxIterationTime) { - Preconditions - .checkArgument(maxIterationTime > 0, "Max iteration time must be positive"); - this.maxIterationTime = maxIterationTime; - return this; - } - - @Nonnull public ChunkCoordinatorBuilder withInitialBatchSize(final int initialBatchSize) { - Preconditions - .checkArgument(initialBatchSize > 0, "Initial batch size must be positive"); - this.initialBatchSize = initialBatchSize; - return this; - } - - @Nonnull public ChunkCoordinatorBuilder withThrowableConsumer( - @Nonnull final Consumer throwableConsumer) { - this.throwableConsumer = - Preconditions.checkNotNull(throwableConsumer, "Throwable consumer may not be null"); - return this; - } - - @Nonnull public ChunkCoordinator build() { - Preconditions.checkNotNull(this.world, "No world was supplied"); - Preconditions.checkNotNull(this.chunkConsumer, "No chunk consumer was supplied"); - Preconditions.checkNotNull(this.whenDone, "No final action was supplied"); - Preconditions - .checkNotNull(this.throwableConsumer, "No throwable consumer was supplied"); - return new ChunkCoordinator(this.maxIterationTime, this.initialBatchSize, - this.chunkConsumer, this.world, this.requestedChunks, this.whenDone, - this.throwableConsumer); - } - - } - -} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java b/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java index 4d10fc316..6c988d52d 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java @@ -27,6 +27,7 @@ package com.plotsquared.bukkit.schematic; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.queue.QueueCoordinator; import com.plotsquared.core.util.SchematicHandler; import com.plotsquared.core.util.WorldUtil; @@ -39,8 +40,8 @@ import javax.annotation.Nonnull; */ @Singleton public class BukkitSchematicHandler extends SchematicHandler { - @Inject public BukkitSchematicHandler(@Nonnull final WorldUtil worldUtil) { - super(worldUtil); + @Inject public BukkitSchematicHandler(@Nonnull final WorldUtil worldUtil, @Nonnull ProgressSubscriberFactory subscriberFactory) { + super(worldUtil, subscriberFactory); } @Override 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 11c98a27b..c7420f0a4 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitRegionManager.java @@ -28,8 +28,10 @@ package com.plotsquared.bukkit.util; import com.google.inject.Inject; import com.google.inject.Singleton; import com.plotsquared.core.generator.AugmentedUtils; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Location; import com.plotsquared.core.location.PlotLoc; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotManager; @@ -53,10 +55,9 @@ import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -72,24 +73,22 @@ import static com.plotsquared.core.util.entity.EntityCategories.CAP_VEHICLE; @Singleton public class BukkitRegionManager extends RegionManager { - private static final Logger logger = - LoggerFactory.getLogger("P2/" + BukkitRegionManager.class.getSimpleName()); private final GlobalBlockQueue blockQueue; - @Inject - public BukkitRegionManager(@Nonnull WorldUtil worldUtil, @Nonnull GlobalBlockQueue blockQueue) { - super(worldUtil, blockQueue); + @Inject public BukkitRegionManager(@Nonnull WorldUtil worldUtil, @Nonnull GlobalBlockQueue blockQueue, @Nonnull + ProgressSubscriberFactory subscriberFactory) { + super(worldUtil, blockQueue, subscriberFactory); this.blockQueue = blockQueue; } - @Override public boolean handleClear(Plot plot, Runnable whenDone, PlotManager manager) { + @Override + public boolean handleClear(@Nonnull Plot plot, @Nullable Runnable whenDone, @Nonnull PlotManager manager, @Nullable PlotPlayer player) { return false; } - @Override public int[] countEntities(Plot plot) { + @Override public int[] countEntities(@Nonnull Plot plot) { int[] existing = (int[]) plot.getMeta("EntityCount"); - if (existing != null && (System.currentTimeMillis() - (long) plot.getMeta("EntityCountTime") - < 1000)) { + if (existing != null && (System.currentTimeMillis() - (long) plot.getMeta("EntityCountTime") < 1000)) { return existing; } PlotArea area = plot.getArea(); @@ -161,8 +160,10 @@ public class BukkitRegionManager extends RegionManager { return count; } - @Override public boolean regenerateRegion(final Location pos1, final Location pos2, - final boolean ignoreAugment, final Runnable whenDone) { + @Override public boolean regenerateRegion(@Nonnull final Location pos1, + @Nonnull final Location pos2, + final boolean ignoreAugment, + @Nullable final Runnable whenDone) { final BukkitWorld world = new BukkitWorld((World) pos1.getWorld()); final int p1x = pos1.getX(); @@ -176,8 +177,7 @@ public class BukkitRegionManager extends RegionManager { final QueueCoordinator queue = blockQueue.getNewQueue(world); final QueueCoordinator regenQueue = blockQueue.getNewQueue(world); - queue.addReadChunks( - new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()).getChunks()); + queue.addReadChunks(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()).getChunks()); queue.setChunkConsumer(chunk -> { int x = chunk.getX(); @@ -187,8 +187,7 @@ public class BukkitRegionManager extends RegionManager { int xxt = xxb + 15; int zzt = zzb + 15; if (xxb >= p1x && xxt <= p2x && zzb >= p1z && zzt <= p2z) { - AugmentedUtils - .bypass(ignoreAugment, () -> regenQueue.regenChunk(chunk.getX(), chunk.getZ())); + AugmentedUtils.bypass(ignoreAugment, () -> regenQueue.regenChunk(chunk.getX(), chunk.getZ())); return; } boolean checkX1 = false; @@ -250,41 +249,37 @@ public class BukkitRegionManager extends RegionManager { if (checkX2 && checkZ2) { map.saveRegion(world, xxt2, xxt, zzt2, zzt); // } - CuboidRegion currentPlotClear = - RegionUtil.createRegion(pos1.getX(), pos2.getX(), pos1.getZ(), pos2.getZ()); - map.saveEntitiesOut(Bukkit.getWorld(world.getName()).getChunkAt(x, z), - currentPlotClear); - AugmentedUtils.bypass(ignoreAugment, () -> ChunkManager - .setChunkInPlotArea(null, new RunnableVal() { - @Override public void run(ScopedQueueCoordinator value) { - Location min = value.getMin(); - int bx = min.getX(); - int bz = min.getZ(); - for (int x1 = 0; x1 < 16; x1++) { - for (int z1 = 0; z1 < 16; z1++) { - PlotLoc plotLoc = new PlotLoc(bx + x1, bz + z1); - BaseBlock[] ids = map.allBlocks.get(plotLoc); - if (ids != null) { - for (int y = 0; y < Math.min(128, ids.length); y++) { - BaseBlock id = ids[y]; - if (id != null) { - value.setBlock(x1, y, z1, id); - } else { - value.setBlock(x1, y, z1, - BlockTypes.AIR.getDefaultState()); - } + CuboidRegion currentPlotClear = RegionUtil.createRegion(pos1.getX(), pos2.getX(), pos1.getZ(), pos2.getZ()); + map.saveEntitiesOut(Bukkit.getWorld(world.getName()).getChunkAt(x, z), currentPlotClear); + AugmentedUtils.bypass(ignoreAugment, () -> ChunkManager.setChunkInPlotArea(null, new RunnableVal() { + @Override public void run(ScopedQueueCoordinator value) { + Location min = value.getMin(); + int bx = min.getX(); + int bz = min.getZ(); + for (int x1 = 0; x1 < 16; x1++) { + for (int z1 = 0; z1 < 16; z1++) { + PlotLoc plotLoc = new PlotLoc(bx + x1, bz + z1); + BaseBlock[] ids = map.allBlocks.get(plotLoc); + if (ids != null) { + for (int y = 0; y < Math.min(128, ids.length); y++) { + BaseBlock id = ids[y]; + if (id != null) { + value.setBlock(x1, y, z1, id); + } else { + value.setBlock(x1, y, z1, BlockTypes.AIR.getDefaultState()); } - for (int y = Math.min(128, ids.length); y < ids.length; y++) { - BaseBlock id = ids[y]; - if (id != null) { - value.setBlock(x1, y, z1, id); - } + } + for (int y = Math.min(128, ids.length); y < ids.length; y++) { + BaseBlock id = ids[y]; + if (id != null) { + value.setBlock(x1, y, z1, id); } } } } } - }, world.getName(), chunk)); + } + }, world.getName(), chunk)); //map.restoreBlocks(worldObj, 0, 0); map.restoreEntities(Bukkit.getWorld(world.getName()), 0, 0); }); @@ -294,7 +289,7 @@ public class BukkitRegionManager extends RegionManager { return true; } - @Override public void clearAllEntities(Location pos1, Location pos2) { + @Override public void clearAllEntities(@Nonnull Location pos1, @Nonnull Location pos2) { String world = pos1.getWorldName(); final World bukkitWorld = BukkitUtil.getWorld(world); @@ -312,8 +307,7 @@ public class BukkitRegionManager extends RegionManager { for (Entity entity : entities) { if (!(entity instanceof Player)) { org.bukkit.Location location = entity.getLocation(); - if (location.getX() >= bx && location.getX() <= tx && location.getZ() >= bz - && location.getZ() <= tz) { + if (location.getX() >= bx && location.getX() <= tx && location.getZ() >= bz && location.getZ() <= tz) { if (entity.hasMetadata("ps-tmp-teleport")) { continue; } @@ -323,17 +317,16 @@ public class BukkitRegionManager extends RegionManager { } } - private void count(int[] count, Entity entity) { - final com.sk89q.worldedit.world.entity.EntityType entityType = - BukkitAdapter.adapt(entity.getType()); + private void count(int[] count, @Nonnull Entity entity) { + final com.sk89q.worldedit.world.entity.EntityType entityType = BukkitAdapter.adapt(entity.getType()); if (EntityCategories.PLAYER.contains(entityType)) { return; - } else if (EntityCategories.PROJECTILE.contains(entityType) || EntityCategories.OTHER - .contains(entityType) || EntityCategories.HANGING.contains(entityType)) { + } else if (EntityCategories.PROJECTILE.contains(entityType) || EntityCategories.OTHER.contains(entityType) || EntityCategories.HANGING + .contains(entityType)) { count[CAP_MISC]++; - } else if (EntityCategories.ANIMAL.contains(entityType) || EntityCategories.VILLAGER - .contains(entityType) || EntityCategories.TAMEABLE.contains(entityType)) { + } else if (EntityCategories.ANIMAL.contains(entityType) || EntityCategories.VILLAGER.contains(entityType) || EntityCategories.TAMEABLE + .contains(entityType)) { count[CAP_MOB]++; count[CAP_ANIMAL]++; } else if (EntityCategories.VEHICLE.contains(entityType)) { diff --git a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java index fc6310ecf..69717bb38 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/BackupProfile.java @@ -25,7 +25,10 @@ */ package com.plotsquared.core.backup; +import com.plotsquared.core.player.PlotPlayer; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -64,8 +67,9 @@ public interface BackupProfile { * Restore a backup * * @param backup Backup to restore + * @param player The player restoring the backup * @return Future that completes when the backup has finished */ - @Nonnull CompletableFuture restoreBackup(@Nonnull final Backup backup); + @Nonnull CompletableFuture restoreBackup(@Nonnull final Backup backup, @Nullable PlotPlayer player); } diff --git a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java index 12460e9c5..0ccc7e054 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/NullBackupProfile.java @@ -25,7 +25,10 @@ */ package com.plotsquared.core.backup; +import com.plotsquared.core.player.PlotPlayer; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.File; import java.nio.file.Path; import java.util.Collections; @@ -53,7 +56,7 @@ public class NullBackupProfile implements BackupProfile { throw new UnsupportedOperationException("Cannot create backup of an unowned plot"); } - @Override @Nonnull public CompletableFuture restoreBackup(@Nonnull final Backup backup) { + @Override @Nonnull public CompletableFuture restoreBackup(@Nonnull final Backup backup, @Nullable PlotPlayer player) { return CompletableFuture.completedFuture(null); } diff --git a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java index 8366fe5bf..e28372ec9 100644 --- a/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java +++ b/Core/src/main/java/com/plotsquared/core/backup/PlayerBackupProfile.java @@ -29,6 +29,7 @@ import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.player.ConsolePlayer; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.schematic.Schematic; import com.plotsquared.core.util.SchematicHandler; @@ -37,6 +38,7 @@ import com.plotsquared.core.util.task.TaskManager; import net.kyori.adventure.text.minimessage.MiniMessage; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -166,7 +168,7 @@ public class PlayerBackupProfile implements BackupProfile { return future; } - @Override @Nonnull public CompletableFuture restoreBackup(@Nonnull final Backup backup) { + @Override @Nonnull public CompletableFuture restoreBackup(@Nonnull final Backup backup, @Nullable PlotPlayer player) { final CompletableFuture future = new CompletableFuture<>(); if (backup.getFile() == null || !Files.exists(backup.getFile())) { future.completeExceptionally(new IllegalArgumentException("The specific backup does not exist")); @@ -181,7 +183,7 @@ public class PlayerBackupProfile implements BackupProfile { if (schematic == null) { future.completeExceptionally(new IllegalArgumentException("The backup is non-existent or not in the correct format")); } else { - this.schematicHandler.paste(schematic, plot, 0, 1, 0, false, new RunnableVal() { + this.schematicHandler.paste(schematic, plot, 0, 1, 0, false, player, new RunnableVal() { @Override public void run(Boolean value) { if (value) { future.complete(null); diff --git a/Core/src/main/java/com/plotsquared/core/command/Backup.java b/Core/src/main/java/com/plotsquared/core/command/Backup.java index bf4f47efe..c2fe616aa 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Backup.java +++ b/Core/src/main/java/com/plotsquared/core/command/Backup.java @@ -314,7 +314,7 @@ public final class Backup extends Command { ); } else { CmdConfirm.addPending(player, "/plot backup load " + number, - () -> backupProfile.restoreBackup(backup) + () -> backupProfile.restoreBackup(backup, player) .whenComplete((n, error) -> { if (error != null) { player.sendMessage( diff --git a/Core/src/main/java/com/plotsquared/core/command/Claim.java b/Core/src/main/java/com/plotsquared/core/command/Claim.java index 50c6ebb6f..92646e6e2 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Claim.java +++ b/Core/src/main/java/com/plotsquared/core/command/Claim.java @@ -50,7 +50,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; -import javax.annotation.Nullable; @CommandDeclaration( command = "claim", @@ -186,7 +185,7 @@ public class Claim extends SubCommand { Template.of("value", "Auto merge on claim") ); } else { - plot.getPlotModificationManager().autoMerge(mergeEvent.getDir(), mergeEvent.getMax(), player.getUUID(), true); + plot.getPlotModificationManager().autoMerge(mergeEvent.getDir(), mergeEvent.getMax(), player.getUUID(), player, true); } } return null; diff --git a/Core/src/main/java/com/plotsquared/core/command/Clear.java b/Core/src/main/java/com/plotsquared/core/command/Clear.java index da40ef248..000f293d3 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Clear.java +++ b/Core/src/main/java/com/plotsquared/core/command/Clear.java @@ -91,7 +91,7 @@ public class Clear extends Command { confirm.run(this, () -> { BackupManager.backup(player, plot, () -> { final long start = System.currentTimeMillis(); - boolean result = plot.getPlotModificationManager().clear(true, false, () -> { + boolean result = plot.getPlotModificationManager().clear(true, false, player, () -> { plot.getPlotModificationManager().unlink(); TaskManager.runTask(() -> { plot.removeRunning(); diff --git a/Core/src/main/java/com/plotsquared/core/command/Condense.java b/Core/src/main/java/com/plotsquared/core/command/Condense.java index 01891ca56..b525cdfc1 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Condense.java +++ b/Core/src/main/java/com/plotsquared/core/command/Condense.java @@ -178,7 +178,7 @@ public class Condense extends SubCommand { i++; final AtomicBoolean result = new AtomicBoolean(false); try { - result.set(origin.getPlotModificationManager().move(possible, () -> { + result.set(origin.getPlotModificationManager().move(possible, player, () -> { if (result.get()) { player.sendMessage( TranslatableCaption.of("condense.moving"), diff --git a/Core/src/main/java/com/plotsquared/core/command/Copy.java b/Core/src/main/java/com/plotsquared/core/command/Copy.java index 64ae43b8a..eac5e7ccc 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Copy.java +++ b/Core/src/main/java/com/plotsquared/core/command/Copy.java @@ -77,7 +77,7 @@ public class Copy extends SubCommand { return false; } - plot1.getPlotModificationManager().copy(plot2).thenAccept(result -> { + plot1.getPlotModificationManager().copy(plot2, player).thenAccept(result -> { if (result) { player.sendMessage(TranslatableCaption.of("move.copy_success")); } else { diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index 8bd023ef8..6ff6ef879 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -98,7 +98,7 @@ public class Delete extends SubCommand { return; } final long start = System.currentTimeMillis(); - boolean result = plot.getPlotModificationManager().deletePlot(() -> { + boolean result = plot.getPlotModificationManager().deletePlot(player, () -> { plot.removeRunning(); if (this.econHandler.isEnabled(plotArea)) { Expression valueExr = plotArea.getPrices().get("sell"); diff --git a/Core/src/main/java/com/plotsquared/core/command/Load.java b/Core/src/main/java/com/plotsquared/core/command/Load.java index 585a41234..05df03a11 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Load.java +++ b/Core/src/main/java/com/plotsquared/core/command/Load.java @@ -26,10 +26,10 @@ package com.plotsquared.core.command; import com.google.inject.Inject; -import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.StaticCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.MetaDataAccess; import com.plotsquared.core.player.PlayerMetaDataKeys; import com.plotsquared.core.player.PlotPlayer; @@ -137,17 +137,16 @@ public class Load extends SubCommand { return; } PlotArea area = plot.getArea(); - this.schematicHandler - .paste(taskSchematic, plot, 0, area.getMinBuildHeight(), 0, false, new RunnableVal() { - @Override public void run(Boolean value) { - plot.removeRunning(); - if (value) { - player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); - } else { - player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed")); - } + this.schematicHandler.paste(taskSchematic, plot, 0, area.getMinBuildHeight(), 0, false, player, new RunnableVal() { + @Override public void run(Boolean value) { + plot.removeRunning(); + if (value) { + player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); + } else { + player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed")); } - }); + } + }); }); return true; } diff --git a/Core/src/main/java/com/plotsquared/core/command/Merge.java b/Core/src/main/java/com/plotsquared/core/command/Merge.java index cbc59fc8f..28e43b1e5 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Merge.java +++ b/Core/src/main/java/com/plotsquared/core/command/Merge.java @@ -180,7 +180,7 @@ public class Merge extends SubCommand { ); return true; } - if (plot.getPlotModificationManager().autoMerge(Direction.ALL, maxSize, uuid, terrain)) { + if (plot.getPlotModificationManager().autoMerge(Direction.ALL, maxSize, uuid, player, terrain)) { if (this.econHandler.isEnabled(plotArea) && price > 0d) { this.econHandler.withdrawMoney(player, price); player.sendMessage( @@ -224,7 +224,7 @@ public class Merge extends SubCommand { ); return true; } - if (plot.getPlotModificationManager().autoMerge(direction, maxSize - size, uuid, terrain)) { + if (plot.getPlotModificationManager().autoMerge(direction, maxSize - size, uuid, player, terrain)) { if (this.econHandler.isEnabled(plotArea) && price > 0d) { this.econHandler.withdrawMoney(player, price); player.sendMessage( @@ -259,7 +259,7 @@ public class Merge extends SubCommand { final Direction dir = direction; Runnable run = () -> { accepter.sendMessage(TranslatableCaption.of("merge.merge_accepted")); - plot.getPlotModificationManager().autoMerge(dir, maxSize - size, owner, terrain); + plot.getPlotModificationManager().autoMerge(dir, maxSize - size, owner, player, terrain); PlotPlayer plotPlayer = PlotSquared.platform().getPlayerManager().getPlayerIfExists(player.getUUID()); if (plotPlayer == null) { accepter.sendMessage(TranslatableCaption.of("merge.merge_not_valid")); diff --git a/Core/src/main/java/com/plotsquared/core/command/Move.java b/Core/src/main/java/com/plotsquared/core/command/Move.java index e4c2a64d3..d8b7a614d 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Move.java +++ b/Core/src/main/java/com/plotsquared/core/command/Move.java @@ -106,7 +106,7 @@ public class Move extends SubCommand { return CompletableFuture.completedFuture(false); } - return plot1.getPlotModificationManager().move(plot2, () -> { + return plot1.getPlotModificationManager().move(plot2, player, () -> { }, false).thenApply(result -> { if (result) { player.sendMessage(TranslatableCaption.of("move.move_success")); diff --git a/Core/src/main/java/com/plotsquared/core/command/Purge.java b/Core/src/main/java/com/plotsquared/core/command/Purge.java index 2d75807b8..1842a5664 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Purge.java +++ b/Core/src/main/java/com/plotsquared/core/command/Purge.java @@ -214,7 +214,7 @@ public class Purge extends SubCommand { try { ids.add(plot.temp); if (finalClear) { - plot.getPlotModificationManager().clear(false, true, () -> { + plot.getPlotModificationManager().clear(false, true, player, () -> { if (Settings.DEBUG) { logger.info("Plot {} cleared by purge", plot.getId()); } diff --git a/Core/src/main/java/com/plotsquared/core/command/SchematicCmd.java b/Core/src/main/java/com/plotsquared/core/command/SchematicCmd.java index 2146c81ab..31bbc2fc1 100644 --- a/Core/src/main/java/com/plotsquared/core/command/SchematicCmd.java +++ b/Core/src/main/java/com/plotsquared/core/command/SchematicCmd.java @@ -27,10 +27,10 @@ package com.plotsquared.core.command; import com.google.common.collect.Lists; import com.google.inject.Inject; -import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.location.Location; +import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; @@ -149,16 +149,16 @@ public class SchematicCmd extends SubCommand { ); return; } - this.schematicHandler.paste(schematic, plot, 0, 1, 0, false, new RunnableVal() { - @Override public void run(Boolean value) { - SchematicCmd.this.running = false; - if (value) { - player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); - } else { - player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed")); - } + this.schematicHandler.paste(schematic, plot, 0, 1, 0, false, player, new RunnableVal() { + @Override public void run(Boolean value) { + SchematicCmd.this.running = false; + if (value) { + player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); + } else { + player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed")); } - }); + } + }); }); break; } diff --git a/Core/src/main/java/com/plotsquared/core/command/Set.java b/Core/src/main/java/com/plotsquared/core/command/Set.java index 3090ed3c5..c0d318c5f 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Set.java +++ b/Core/src/main/java/com/plotsquared/core/command/Set.java @@ -26,10 +26,12 @@ package com.plotsquared.core.command; import com.google.inject.Inject; +import com.plotsquared.core.PlotSquared; import com.plotsquared.core.backup.BackupManager; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.StaticCaption; import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; @@ -45,19 +47,18 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.world.block.BlockCategory; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; -import java.util.Collections; -import java.util.LinkedList; import net.kyori.adventure.text.minimessage.Template; import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; -import java.util.stream.Stream; @CommandDeclaration(command = "set", aliases = {"s"}, @@ -156,9 +157,16 @@ public class Set extends SubCommand { plot.addRunning(); QueueCoordinator queue = plotArea.getQueue(); for (final Plot current : plot.getConnectedPlots()) { - current.getPlotModificationManager().setComponent(component, pattern, queue); + current.getPlotModificationManager().setComponent(component, pattern, player, queue); + } + queue.setCompleteTask(() -> { + plot.removeRunning(); + player.sendMessage(TranslatableCaption.of("working.component_complete")); + }); + if (Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber( + PlotSquared.platform().getInjector().getInstance(ProgressSubscriberFactory.class).createWithActor(player)); } - queue.setCompleteTask(plot::removeRunning); queue.enqueue(); player.sendMessage(TranslatableCaption.of("working.generating_component")); }); diff --git a/Core/src/main/java/com/plotsquared/core/command/Swap.java b/Core/src/main/java/com/plotsquared/core/command/Swap.java index 6bb379c6c..3708dac2e 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Swap.java +++ b/Core/src/main/java/com/plotsquared/core/command/Swap.java @@ -84,7 +84,7 @@ public class Swap extends SubCommand { return CompletableFuture.completedFuture(false); } - return plot1.getPlotModificationManager().move(plot2, () -> { + return plot1.getPlotModificationManager().move(plot2, player, () -> { }, true).thenApply(result -> { if (result) { player.sendMessage(TranslatableCaption.of("swap.swap_success")); diff --git a/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java b/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java index e775892da..94c3177b7 100644 --- a/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java +++ b/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java @@ -194,7 +194,7 @@ public class ComponentPresetManager { plot.addRunning(); QueueCoordinator queue = plot.getArea().getQueue(); for (Plot current : plot.getConnectedPlots()) { - current.getPlotModificationManager().setComponent(componentPreset.getComponent().name(), pattern, queue); + current.getPlotModificationManager().setComponent(componentPreset.getComponent().name(), pattern, player, queue); } queue.setCompleteTask(plot::removeRunning); queue.enqueue(); diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 6c8b4cb58..bfa462404 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -529,8 +529,24 @@ public class Settings extends Config { @Comment("Settings relating to PlotSquared's GlobalBlockQueue") public static final class QUEUE { @Comment({"Average time per tick spent completing chunk tasks in ms.", - "Waits (chunk task time / target_time) ticks before completely the next task."}) - public static int TARGET_TIME = 65; + "Queue will adjust the batch size to match this."}) + public static int MAX_ITERATION_TIME = 30; + @Comment({"Initial number of chunks to process by the queue. This can be increased or", + "decreased by the queue based on the actual iteration time compared to above."}) + public static int INITIAL_BATCH_SIZE = 5; + @Comment("Notify progress of the queue to the player or console.") + public static boolean NOTIFY_PROGRESS = true; + @Comment("Interval in ms to notify player or console of progress.") + public static int NOTIFY_INTERVAL = 5000; + @Comment({"Time to wait in ms before beginning to notify player or console of progress.", + "Prevent needless notification of progress for short queues."}) + public static int NOTIFY_WAIT = 5000; + @Comment({"How lighitng should be handled by the queue. Modes:", + " - 0 - Do not do any lighting (fastest)", + " - 1 - Only execute lighting where blocks with light values are placed", + " - 2 - Only execute lighting where blocks with light values are placed or removed/replaced", + " - 3 - Always execute lighting (slowest)"}) + public static int LIGHTING_MODE = 1; } @Comment("Settings related to tab completion") diff --git a/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java b/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java index ca8b5013a..9ef766465 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java @@ -25,9 +25,13 @@ */ package com.plotsquared.core.generator; +import com.google.inject.Inject; +import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Direction; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.BlockBucket; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotAreaTerrainType; @@ -53,44 +57,49 @@ public class ClassicPlotManager extends SquarePlotManager { private final ClassicPlotWorld classicPlotWorld; private final RegionManager regionManager; + private final ProgressSubscriberFactory subscriberFactory; - public ClassicPlotManager(@Nonnull final ClassicPlotWorld classicPlotWorld, @Nonnull final RegionManager regionManager) { + @Inject public ClassicPlotManager(@Nonnull final ClassicPlotWorld classicPlotWorld, @Nonnull final RegionManager regionManager) { super(classicPlotWorld, regionManager); this.classicPlotWorld = classicPlotWorld; this.regionManager = regionManager; + this.subscriberFactory = PlotSquared.platform().getInjector().getInstance(ProgressSubscriberFactory.class); } - @Override - public boolean setComponent(@Nonnull PlotId plotId, @Nonnull String component, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + @Override public boolean setComponent(@Nonnull PlotId plotId, + @Nonnull String component, + @Nonnull Pattern blocks, + @Nullable PlotPlayer actor, + @Nullable QueueCoordinator queue) { final Optional componentOptional = ClassicPlotManagerComponent.fromString(component); if (componentOptional.isPresent()) { switch (componentOptional.get()) { case FLOOR: - return setFloor(plotId, blocks, queue); + return setFloor(plotId, blocks, actor, queue); case WALL: - return setWallFilling(plotId, blocks, queue); + return setWallFilling(plotId, blocks, actor, queue); case AIR: - return setAir(plotId, blocks, queue); + return setAir(plotId, blocks, actor, queue); case MAIN: - return setMain(plotId, blocks, queue); + return setMain(plotId, blocks, actor, queue); case MIDDLE: return setMiddle(plotId, blocks, queue); case OUTLINE: - return setOutline(plotId, blocks, queue); + return setOutline(plotId, blocks, actor, queue); case BORDER: - return setWall(plotId, blocks, queue); + return setWall(plotId, blocks, actor, queue); case ALL: - return setAll(plotId, blocks, queue); + return setAll(plotId, blocks, actor, queue); } } return false; } @Override public boolean unClaimPlot(@Nonnull Plot plot, @Nullable Runnable whenDone, @Nullable QueueCoordinator queue) { - setWallFilling(plot.getId(), classicPlotWorld.WALL_FILLING.toPattern(), queue); + setWallFilling(plot.getId(), classicPlotWorld.WALL_FILLING.toPattern(), null, queue); if (classicPlotWorld.PLACE_TOP_BLOCK && (!classicPlotWorld.WALL_BLOCK.isAir() || !classicPlotWorld.WALL_BLOCK .equals(classicPlotWorld.CLAIMED_WALL_BLOCK))) { - setWall(plot.getId(), classicPlotWorld.WALL_BLOCK.toPattern(), queue); + setWall(plot.getId(), classicPlotWorld.WALL_BLOCK.toPattern(), null, queue); } TaskManager.runTask(whenDone); return true; @@ -105,11 +114,11 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setFloor(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setFloor(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { Plot plot = classicPlotWorld.getPlotAbs(plotId); if (plot != null && plot.isBasePlot()) { return this.regionManager - .setCuboids(classicPlotWorld, plot.getRegions(), blocks, classicPlotWorld.PLOT_HEIGHT, classicPlotWorld.PLOT_HEIGHT, queue); + .setCuboids(classicPlotWorld, plot.getRegions(), blocks, classicPlotWorld.PLOT_HEIGHT, classicPlotWorld.PLOT_HEIGHT, actor, queue); } return false; } @@ -123,10 +132,10 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setAll(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setAll(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { Plot plot = classicPlotWorld.getPlotAbs(plotId); if (plot != null && plot.isBasePlot()) { - return this.regionManager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, getWorldHeight(), queue); + return this.regionManager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, getWorldHeight(), actor, queue); } return false; } @@ -140,11 +149,11 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setAir(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setAir(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { Plot plot = classicPlotWorld.getPlotAbs(plotId); if (plot != null && plot.isBasePlot()) { return this.regionManager - .setCuboids(classicPlotWorld, plot.getRegions(), blocks, classicPlotWorld.PLOT_HEIGHT + 1, getWorldHeight(), queue); + .setCuboids(classicPlotWorld, plot.getRegions(), blocks, classicPlotWorld.PLOT_HEIGHT + 1, getWorldHeight(), actor, queue); } return false; } @@ -158,10 +167,10 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setMain(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setMain(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { Plot plot = classicPlotWorld.getPlotAbs(plotId); if (plot == null || plot.isBasePlot()) { - return this.regionManager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, classicPlotWorld.PLOT_HEIGHT - 1, queue); + return this.regionManager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, classicPlotWorld.PLOT_HEIGHT - 1, actor, queue); } return false; } @@ -203,7 +212,7 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setOutline(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setOutline(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { if (classicPlotWorld.ROAD_WIDTH == 0) { return false; } @@ -224,6 +233,9 @@ public class ClassicPlotManager extends SquarePlotManager { if (queue == null) { queue = classicPlotWorld.getQueue(); enqueue = true; + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } } int maxY = classicPlotWorld.getPlotManager().getWorldHeight(); @@ -279,7 +291,7 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setWallFilling(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setWallFilling(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { if (classicPlotWorld.ROAD_WIDTH == 0) { return false; } @@ -300,6 +312,9 @@ public class ClassicPlotManager extends SquarePlotManager { if (queue == null) { queue = classicPlotWorld.getQueue(); enqueue = true; + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } } if (!plot.isMerged(Direction.NORTH)) { @@ -346,7 +361,7 @@ public class ClassicPlotManager extends SquarePlotManager { * otherwise writes to the queue but does not enqueue. * @return success or not */ - public boolean setWall(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + public boolean setWall(@Nonnull PlotId plotId, @Nonnull Pattern blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { if (classicPlotWorld.ROAD_WIDTH == 0) { return false; } @@ -367,6 +382,9 @@ public class ClassicPlotManager extends SquarePlotManager { if (queue == null) { enqueue = true; queue = classicPlotWorld.getQueue(); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } } int y = classicPlotWorld.WALL_HEIGHT + 1; @@ -578,13 +596,13 @@ public class ClassicPlotManager extends SquarePlotManager { final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK; if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) { for (PlotId plotId : plotIds) { - setWall(plotId, claim.toPattern(), queue); + setWall(plotId, claim.toPattern(), null, queue); } } if (Settings.General.MERGE_REPLACE_WALL) { final BlockBucket wallBlock = classicPlotWorld.WALL_FILLING; for (PlotId id : plotIds) { - setWallFilling(id, wallBlock.toPattern(), queue); + setWallFilling(id, wallBlock.toPattern(), null, queue); } } return true; @@ -594,7 +612,7 @@ public class ClassicPlotManager extends SquarePlotManager { final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK; if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) { for (PlotId id : plotIds) { - setWall(id, claim.toPattern(), queue); + setWall(id, claim.toPattern(), null, queue); } } return true; // return false if unlink has been denied @@ -611,7 +629,7 @@ public class ClassicPlotManager extends SquarePlotManager { @Override public boolean claimPlot(@Nonnull Plot plot, @Nullable QueueCoordinator queue) { final BlockBucket claim = classicPlotWorld.CLAIMED_WALL_BLOCK; if (classicPlotWorld.PLACE_TOP_BLOCK && (!claim.isAir() || !claim.equals(classicPlotWorld.WALL_BLOCK))) { - return setWall(plot.getId(), claim.toPattern(), queue); + return setWall(plot.getId(), claim.toPattern(), null, queue); } return true; } diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java index 95d330057..11de8c99f 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java @@ -29,7 +29,9 @@ import com.google.common.collect.Sets; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.command.Template; import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotAreaTerrainType; import com.plotsquared.core.plot.PlotAreaType; @@ -61,11 +63,15 @@ public class HybridPlotManager extends ClassicPlotManager { private final HybridPlotWorld hybridPlotWorld; private final RegionManager regionManager; + private final ProgressSubscriberFactory subscriberFactory; - public HybridPlotManager(@Nonnull final HybridPlotWorld hybridPlotWorld, @Nonnull final RegionManager regionManager) { + public HybridPlotManager(@Nonnull final HybridPlotWorld hybridPlotWorld, + @Nonnull final RegionManager regionManager, + @Nonnull ProgressSubscriberFactory subscriberFactory) { super(hybridPlotWorld, regionManager); this.hybridPlotWorld = hybridPlotWorld; this.regionManager = regionManager; + this.subscriberFactory = subscriberFactory; } @Override public void exportTemplate() throws IOException { @@ -199,21 +205,16 @@ public class HybridPlotManager extends ClassicPlotManager { return !enqueue || queue.enqueue(); } - /** - *

Clearing the plot needs to only consider removing the blocks - This implementation has - * used the setCuboidAsync function, as it is fast, and uses NMS code - It also makes use of the - * fact that deleting chunks is a lot faster than block updates This code is very messy, but you - * don't need to do something quite as complex unless you happen to have 512x512 sized plots. - *

- */ - @Override public boolean clearPlot(@Nonnull final Plot plot, @Nullable final Runnable whenDone, @Nullable QueueCoordinator queue) { + @Override public boolean clearPlot(@Nonnull final Plot plot, + @Nullable final Runnable whenDone, + @Nullable PlotPlayer actor, + @Nullable QueueCoordinator queue) { if (this.regionManager.notifyClear(this)) { //If this returns false, the clear didn't work - if (this.regionManager.handleClear(plot, whenDone, this)) { + if (this.regionManager.handleClear(plot, whenDone, this, actor)) { return true; } } - final String world = hybridPlotWorld.getWorldName(); final Location pos1 = plot.getBottomAbs(); final Location pos2 = plot.getExtendedTopAbs(); // If augmented @@ -248,6 +249,9 @@ public class HybridPlotManager extends ClassicPlotManager { queue.setRegenRegion(new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3())); } pastePlotSchematic(queue, pos1, pos2); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } if (whenDone != null) { queue.setCompleteTask(whenDone); } diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotWorld.java b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotWorld.java index cff9f47d9..3062e8ea6 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotWorld.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotWorld.java @@ -31,6 +31,7 @@ import com.plotsquared.core.configuration.ConfigurationSection; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.file.YamlConfiguration; import com.plotsquared.core.inject.annotations.WorldConfig; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Location; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; @@ -138,7 +139,8 @@ public class HybridPlotWorld extends ClassicPlotWorld { } @Nonnull @Override protected PlotManager createManager() { - return new HybridPlotManager(this, PlotSquared.platform().getRegionManager()); + return new HybridPlotManager(this, PlotSquared.platform().getRegionManager(), + PlotSquared.platform().getInjector().getInstance(ProgressSubscriberFactory.class)); } public Location getSignLocation(@Nonnull Plot plot) { diff --git a/Core/src/main/java/com/plotsquared/core/generator/SquarePlotManager.java b/Core/src/main/java/com/plotsquared/core/generator/SquarePlotManager.java index 54104cae5..13398ea7b 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/SquarePlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/generator/SquarePlotManager.java @@ -27,6 +27,7 @@ package com.plotsquared.core.generator; import com.plotsquared.core.location.Direction; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotId; @@ -58,7 +59,10 @@ public abstract class SquarePlotManager extends GridPlotManager { this.regionManager = regionManager; } - @Override public boolean clearPlot(final @Nonnull Plot plot, final @Nullable Runnable whenDone, @Nullable QueueCoordinator queue) { + @Override public boolean clearPlot(final @Nonnull Plot plot, + final @Nullable Runnable whenDone, + @Nullable PlotPlayer actor, + @Nullable QueueCoordinator queue) { final Set regions = plot.getRegions(); Runnable run = new Runnable() { @Override public void run() { diff --git a/Core/src/main/java/com/plotsquared/core/inject/factory/ChunkCoordinatorFactory.java b/Core/src/main/java/com/plotsquared/core/inject/factory/ChunkCoordinatorFactory.java index cda2e8e34..758b87328 100644 --- a/Core/src/main/java/com/plotsquared/core/inject/factory/ChunkCoordinatorFactory.java +++ b/Core/src/main/java/com/plotsquared/core/inject/factory/ChunkCoordinatorFactory.java @@ -26,6 +26,7 @@ package com.plotsquared.core.inject.factory; import com.plotsquared.core.queue.ChunkCoordinator; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.world.World; @@ -42,6 +43,7 @@ public interface ChunkCoordinatorFactory { @Nonnull final Collection requestedChunks, @Nonnull final Runnable whenDone, @Nonnull final Consumer throwableConsumer, - final boolean unloadAfter); + final boolean unloadAfter, + @Nonnull final Collection progressSubscribers); } diff --git a/Core/src/main/java/com/plotsquared/core/inject/factory/ProgressSubscriberFactory.java b/Core/src/main/java/com/plotsquared/core/inject/factory/ProgressSubscriberFactory.java new file mode 100644 index 000000000..7abc60daa --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/inject/factory/ProgressSubscriberFactory.java @@ -0,0 +1,47 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.inject.factory; + +import com.google.inject.assistedinject.Assisted; +import com.plotsquared.core.configuration.caption.Caption; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface ProgressSubscriberFactory { + + @Nonnull ProgressSubscriber create(); + + @Nonnull ProgressSubscriber createWithActor(@Nullable @Assisted("subscriber") PlotPlayer actor); + + @Nonnull ProgressSubscriber createFull(@Nullable @Assisted("subscriber") PlotPlayer actor, + @Assisted("progressInterval") final long interval, + @Assisted("waitBeforeStarting") final long wait, + @Nullable @Assisted("caption") Caption caption); + +} diff --git a/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java b/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java index 82c5549c3..af22b5ffc 100644 --- a/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java +++ b/Core/src/main/java/com/plotsquared/core/inject/modules/PlotSquaredModule.java @@ -26,6 +26,7 @@ package com.plotsquared.core.inject.modules; import com.google.inject.AbstractModule; +import com.google.inject.assistedinject.FactoryModuleBuilder; import com.intellectualsites.services.ServicePipeline; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.file.YamlConfiguration; @@ -34,7 +35,10 @@ import com.plotsquared.core.inject.annotations.ConfigFile; import com.plotsquared.core.inject.annotations.ImpromptuPipeline; import com.plotsquared.core.inject.annotations.WorldConfig; import com.plotsquared.core.inject.annotations.WorldFile; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.listener.PlotListener; +import com.plotsquared.core.queue.subscriber.DefaultProgressSubscriber; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.plotsquared.core.util.EventDispatcher; import com.plotsquared.core.uuid.UUIDPipeline; import com.sk89q.worldedit.WorldEdit; diff --git a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java index fe17a7899..611cff034 100644 --- a/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java +++ b/Core/src/main/java/com/plotsquared/core/player/PlotPlayer.java @@ -575,7 +575,7 @@ public abstract class PlotPlayer

implements CommandCaller, OfflinePlotPlayer, } if (Settings.Enabled_Components.BAN_DELETER && isBanned()) { for (Plot owned : getPlots()) { - owned.getPlotModificationManager().deletePlot(null); + owned.getPlotModificationManager().deletePlot(null, null); if (Settings.DEBUG) { logger.info("Plot {} was deleted + cleared due to {} getting banned", owned.getId(), getName()); } 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 0400c1e8a..d9c64417f 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/Plot.java +++ b/Core/src/main/java/com/plotsquared/core/plot/Plot.java @@ -1624,7 +1624,7 @@ public class Plot { e.printStackTrace(); return true; } - schematicHandler.paste(sch, this, 0, 1, 0, Settings.Schematics.PASTE_ON_TOP, new RunnableVal() { + schematicHandler.paste(sch, this, 0, 1, 0, Settings.Schematics.PASTE_ON_TOP, player, new RunnableVal() { @Override public void run(Boolean value) { if (value) { player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotManager.java b/Core/src/main/java/com/plotsquared/core/plot/PlotManager.java index 21b810072..3dc8c6a36 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotManager.java @@ -28,6 +28,7 @@ package com.plotsquared.core.plot; import com.plotsquared.core.command.Template; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.queue.QueueCoordinator; import com.plotsquared.core.util.FileBytes; import com.sk89q.worldedit.function.pattern.Pattern; @@ -61,7 +62,10 @@ public abstract class PlotManager { // the same applies here public abstract Location getPlotTopLocAbs(@Nonnull PlotId plotId); - public abstract boolean clearPlot(@Nonnull Plot plot, @Nullable Runnable whenDone, @Nullable QueueCoordinator queue); + public abstract boolean clearPlot(@Nonnull Plot plot, + @Nullable Runnable whenDone, + @Nullable PlotPlayer actor, + @Nullable QueueCoordinator queue); public abstract boolean claimPlot(@Nonnull Plot plot, @Nullable QueueCoordinator queue); @@ -98,6 +102,7 @@ public abstract class PlotManager { * @param plotId id of plot to set component to * @param component FLOOR, WALL, AIR, MAIN, MIDDLE, OUTLINE, BORDER, ALL (floor, air and main). * @param blocks Pattern to set component to + * @param actor The player executing the task * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, * otherwise writes to the queue but does not enqueue. * @return success or not @@ -105,6 +110,7 @@ public abstract class PlotManager { public abstract boolean setComponent(@Nonnull PlotId plotId, @Nonnull String component, @Nonnull Pattern blocks, + @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue); /** diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotModificationManager.java b/Core/src/main/java/com/plotsquared/core/plot/PlotModificationManager.java index cf56fe1a3..138c66e14 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotModificationManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotModificationManager.java @@ -25,8 +25,10 @@ */ package com.plotsquared.core.plot; +import com.google.inject.Inject; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.ConfigurationUtil; +import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.Caption; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.database.DBFunc; @@ -35,6 +37,7 @@ import com.plotsquared.core.events.PlotMergeEvent; import com.plotsquared.core.events.PlotUnlinkEvent; import com.plotsquared.core.events.Result; import com.plotsquared.core.generator.SquarePlotWorld; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Direction; import com.plotsquared.core.location.Location; import com.plotsquared.core.player.PlotPlayer; @@ -75,19 +78,21 @@ public final class PlotModificationManager { private static final Logger logger = LoggerFactory.getLogger("P2/" + PlotModificationManager.class.getSimpleName()); private final Plot plot; + private final ProgressSubscriberFactory subscriberFactory; - PlotModificationManager(@Nonnull final Plot plot) { + @Inject PlotModificationManager(@Nonnull final Plot plot) { this.plot = plot; + this.subscriberFactory = PlotSquared.platform().getInjector().getInstance(ProgressSubscriberFactory.class); } - /** * Copy a plot to a location, both physically and the settings * * @param destination destination plot + * @param actor the actor associated with the copy * @return Future that completes with {@code true} if the copy was successful, else {@code false} */ - public CompletableFuture copy(@Nonnull final Plot destination) { + public CompletableFuture copy(@Nonnull final Plot destination, @Nullable PlotPlayer actor) { final CompletableFuture future = new CompletableFuture<>(); final PlotId offset = PlotId.of(destination.getId().getX() - this.plot.getId().getX(), destination.getId().getY() - this.plot.getId().getY()); final Location db = destination.getBottomAbs(); @@ -169,7 +174,7 @@ public final class PlotModificationManager { Location pos1 = corners[0]; Location pos2 = corners[1]; Location newPos = pos1.add(offsetX, 0, offsetZ).withWorld(destination.getWorldName()); - PlotSquared.platform().getRegionManager().copyRegion(pos1, pos2, newPos, this); + PlotSquared.platform().getRegionManager().copyRegion(pos1, pos2, newPos, actor, this); } }; run.run(); @@ -180,22 +185,26 @@ public final class PlotModificationManager { * Clear the plot * * @param whenDone A runnable to execute when clearing finishes, or null - * @see #clear(boolean, boolean, Runnable) - * @see #deletePlot(Runnable) to clear and delete a plot + * @see #clear(boolean, boolean, PlotPlayer, Runnable) + * @see #deletePlot(PlotPlayer, Runnable) to clear and delete a plot */ public void clear(@Nullable final Runnable whenDone) { - this.clear(false, false, whenDone); + this.clear(false, false, null, whenDone); } /** * Clear the plot * * @param checkRunning Whether or not already executing tasks should be checked - * @param isDelete Whether or not the plot is being deleted - * @param whenDone A runnable to execute when clearing finishes, or null - * @see #deletePlot(Runnable) to clear and delete a plot + * @param isDelete Whether or not the plot is being deleted + * @param actor The actor clearing the plot + * @param whenDone A runnable to execute when clearing finishes, or null + * @see #deletePlot(PlotPlayer, Runnable) to clear and delete a plot */ - public boolean clear(final boolean checkRunning, final boolean isDelete, @Nullable final Runnable whenDone) { + public boolean clear(final boolean checkRunning, + final boolean isDelete, + @Nullable final PlotPlayer actor, + @Nullable final Runnable whenDone) { if (checkRunning && this.plot.getRunning() != 0) { return false; } @@ -230,9 +239,9 @@ public final class PlotModificationManager { } } if (queue.size() > 0) { + queue.setCompleteTask(run); queue.enqueue(); } - TaskManager.runTask(run); return; } Plot current = queue.poll(); @@ -245,7 +254,7 @@ public final class PlotModificationManager { } return; } - manager.clearPlot(current, this, null); + manager.clearPlot(current, this, actor, null); } }; run.run(); @@ -352,11 +361,8 @@ public final class PlotModificationManager { if (this.plot.getArea().allowSigns()) { Location location = manager.getSignLoc(this.plot); String id = this.plot.getId().toString(); - Caption[] lines = - new Caption[] {TranslatableCaption.of("signs.owner_sign_line_1"), - TranslatableCaption.of("signs.owner_sign_line_2"), - TranslatableCaption.of("signs.owner_sign_line_3"), - TranslatableCaption.of("signs.owner_sign_line_4")}; + Caption[] lines = new Caption[] {TranslatableCaption.of("signs.owner_sign_line_1"), TranslatableCaption.of("signs.owner_sign_line_2"), + TranslatableCaption.of("signs.owner_sign_line_3"), TranslatableCaption.of("signs.owner_sign_line_4")}; PlotSquared.platform().getWorldUtil().setSign(location, lines, Template.of("id", id), Template.of("owner", name)); } } @@ -387,8 +393,8 @@ public final class PlotModificationManager { return; } Location location = manager.getSignLoc(this.plot); - QueueCoordinator queue = PlotSquared.platform().getGlobalBlockQueue() - .getNewQueue(PlotSquared.platform().getWorldUtil().getWeWorld(this.plot.getWorldName())); + QueueCoordinator queue = + PlotSquared.platform().getGlobalBlockQueue().getNewQueue(PlotSquared.platform().getWorldUtil().getWeWorld(this.plot.getWorldName())); queue.setBlock(location.getX(), location.getY(), location.getZ(), BlockTypes.AIR.getDefaultState()); queue.enqueue(); } @@ -450,17 +456,15 @@ public final class PlotModificationManager { if (notify && plotworld.isAutoMerge()) { final PlotPlayer player = PlotSquared.platform().getPlayerManager().getPlayerIfExists(uuid); - PlotMergeEvent - event = PlotSquared.get().getEventDispatcher().callMerge(this.plot, Direction.ALL, Integer.MAX_VALUE, player); + PlotMergeEvent event = PlotSquared.get().getEventDispatcher().callMerge(this.plot, Direction.ALL, Integer.MAX_VALUE, player); if (event.getEventResult() == Result.DENY) { if (player != null) { - player.sendMessage(TranslatableCaption.of("events.event_denied"), - Template.of("value", "Auto merge on claim")); + player.sendMessage(TranslatableCaption.of("events.event_denied"), Template.of("value", "Auto merge on claim")); } return; } - plot.getPlotModificationManager().autoMerge(event.getDir(), event.getMax(), uuid, true); + plot.getPlotModificationManager().autoMerge(event.getDir(), event.getMax(), uuid, player, true); } }); return true; @@ -495,10 +499,15 @@ public final class PlotModificationManager { * @param dir the direction to merge * @param max the max number of merges to do * @param uuid the UUID it is allowed to merge with + * @param actor The actor executing the task * @param removeRoads whether to remove roads * @return {@code true} if a merge takes place, else {@code false} */ - public boolean autoMerge(@Nonnull final Direction dir, int max, @Nonnull final UUID uuid, final boolean removeRoads) { + public boolean autoMerge(@Nonnull final Direction dir, + int max, + @Nonnull final UUID uuid, + @Nullable PlotPlayer actor, + final boolean removeRoads) { //Ignore merging if there is no owner for the plot if (!this.plot.hasOwner()) { return false; @@ -584,6 +593,9 @@ public final class PlotModificationManager { } } } + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } if (queue.size() > 0) { queue.enqueue(); } @@ -595,11 +607,13 @@ public final class PlotModificationManager { * Moves a plot physically, as well as the corresponding settings. * * @param destination Plot moved to + * @param actor The actor executing the task * @param whenDone task when done * @param allowSwap whether to swap plots * @return {@code true} if the move was successful, else {@code false} */ @Nonnull public CompletableFuture move(@Nonnull final Plot destination, + @Nullable final PlotPlayer actor, @Nonnull final Runnable whenDone, final boolean allowSwap) { final PlotId offset = PlotId.of(destination.getId().getX() - this.plot.getId().getX(), destination.getId().getY() - this.plot.getId().getY()); @@ -669,7 +683,7 @@ public final class PlotModificationManager { Location pos1 = corners[0]; Location pos2 = corners[1]; Location pos3 = pos1.add(offsetX, 0, offsetZ).withWorld(destination.getWorldName()); - PlotSquared.platform().getRegionManager().swap(pos1, pos2, pos3, this); + PlotSquared.platform().getRegionManager().swap(pos1, pos2, pos3, actor, this); } } }.run(); @@ -678,7 +692,8 @@ public final class PlotModificationManager { @Override public void run() { if (regions.isEmpty()) { Plot plot = destination.getRelative(0, 0); - Plot originPlot = originArea.getPlotAbs(PlotId.of(plot.getId().getX() - offset.getX(), plot.getId().getY() - offset.getY())); + Plot originPlot = + originArea.getPlotAbs(PlotId.of(plot.getId().getX() - offset.getX(), plot.getId().getY() - offset.getY())); final Runnable clearDone = () -> { QueueCoordinator queue = PlotModificationManager.this.plot.getArea().getQueue(); for (final Plot current : plot.getConnectedPlots()) { @@ -691,7 +706,7 @@ public final class PlotModificationManager { TaskManager.runTask(whenDone); }; if (originPlot != null) { - originPlot.getPlotModificationManager().clear(false, true, clearDone); + originPlot.getPlotModificationManager().clear(false, true, actor, clearDone); } else { clearDone.run(); } @@ -703,7 +718,7 @@ public final class PlotModificationManager { final Location pos1 = corners[0]; final Location pos2 = corners[1]; Location newPos = pos1.add(offsetX, 0, offsetZ).withWorld(destination.getWorldName()); - PlotSquared.platform().getRegionManager().copyRegion(pos1, pos2, newPos, task); + PlotSquared.platform().getRegionManager().copyRegion(pos1, pos2, newPos, actor, task); } }.run(); } @@ -726,11 +741,14 @@ public final class PlotModificationManager { * - The destination must correspond to a valid plot of equal dimensions * * @param destination The other plot to swap with + * @param actor The actor executing the task * @param whenDone A task to run when finished, or null * @return Future that completes with {@code true} if the swap was successful, else {@code false} */ - @Nonnull public CompletableFuture swap(@Nonnull final Plot destination, @Nonnull final Runnable whenDone) { - return this.move(destination, whenDone, true); + @Nonnull public CompletableFuture swap(@Nonnull final Plot destination, + @Nullable PlotPlayer actor, + @Nonnull final Runnable whenDone) { + return this.move(destination, actor, whenDone, true); } /** @@ -738,11 +756,14 @@ public final class PlotModificationManager { * - The location must be empty * * @param destination Where to move the plot + * @param actor The actor executing the task * @param whenDone A task to run when done, or null * @return Future that completes with {@code true} if the move was successful, else {@code false} */ - @Nonnull public CompletableFuture move(@Nonnull final Plot destination, @Nonnull final Runnable whenDone) { - return this.move(destination, whenDone, false); + @Nonnull public CompletableFuture move(@Nonnull final Plot destination, + @Nullable PlotPlayer actor, + @Nonnull final Runnable whenDone) { + return this.move(destination, actor, whenDone, false); } /** @@ -752,31 +773,34 @@ public final class PlotModificationManager { * * @param component Component to set * @param blocks Pattern to use the generation - * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, - * otherwise writes to the queue but does not enqueue. + * @param actor The actor executing the task + * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, + * otherwise writes to the queue but does not enqueue. * @return {@code true} if the component was set successfully, else {@code false} */ - public boolean setComponent(@Nonnull final String component, @Nonnull final Pattern blocks, @Nullable final QueueCoordinator queue) { + public boolean setComponent(@Nonnull final String component, + @Nonnull final Pattern blocks, + @Nullable PlotPlayer actor, + @Nullable final QueueCoordinator queue) { final PlotComponentSetEvent event = PlotSquared.get().getEventDispatcher().callComponentSet(this.plot, component, blocks); - return this.plot.getManager().setComponent(this.plot.getId(), event.getComponent(), event.getPattern(), queue); + return this.plot.getManager().setComponent(this.plot.getId(), event.getComponent(), event.getPattern(), actor, queue); } /** * Delete a plot (use null for the runnable if you don't need to be notified on completion) * - * @see PlotSquared#removePlot(Plot, boolean) - * @see PlotModificationManager#clear(boolean, boolean, Runnable) to simply clear a plot - * + * @param actor The actor executing the task * @param whenDone task to run when plot has been deleted. Nullable - * * @return {@code true} if the deletion was successful, {@code false} if not + * @see PlotSquared#removePlot(Plot, boolean) + * @see PlotModificationManager#clear(boolean, boolean, PlotPlayer, Runnable) to simply clear a plot */ - public boolean deletePlot(final Runnable whenDone) { + public boolean deletePlot(@Nullable PlotPlayer actor, final Runnable whenDone) { if (!this.plot.hasOwner()) { return false; } final Set plots = this.plot.getConnectedPlots(); - this.clear(false, true, () -> { + this.clear(false, true, actor, () -> { for (Plot current : plots) { current.unclaim(); } @@ -785,23 +809,24 @@ public final class PlotModificationManager { return true; } + /** /** * Sets components such as border, wall, floor. * (components are generator specific) * * @param component component to set - * @param blocks string of block(s) to set component to - * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, - * otherwise writes to the queue but does not enqueue. - * + * @param blocks string of block(s) to set component to + * @param actor The player executing the task + * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, + * otherwise writes to the queue but does not enqueue. * @return {@code true} if the update was successful, {@code false} if not */ - @Deprecated public boolean setComponent(String component, String blocks, QueueCoordinator queue) { + @Deprecated public boolean setComponent(String component, String blocks, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { final BlockBucket parsed = ConfigurationUtil.BLOCK_BUCKET.parseString(blocks); if (parsed != null && parsed.isEmpty()) { return false; } - return this.setComponent(component, parsed.toPattern(), queue); + return this.setComponent(component, parsed.toPattern(), actor, queue); } /** diff --git a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java index 65e9c876c..14120818f 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/expiration/ExpireManager.java @@ -426,7 +426,7 @@ public class ExpireManager { Templates.of("plot", plot.toString())); } } - plot.getPlotModificationManager().deletePlot(whenDone); + plot.getPlotModificationManager().deletePlot(null, whenDone); } public long getAge(UUID uuid) { diff --git a/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotManager.java b/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotManager.java index 756409612..5f9a5ba07 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/plot/world/SinglePlotManager.java @@ -27,6 +27,7 @@ package com.plotsquared.core.plot.world; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotId; @@ -63,7 +64,7 @@ public class SinglePlotManager extends PlotManager { return Location.at(plotId.toCommaSeparatedString(), 30000000, 0, 30000000); } - @Override public boolean clearPlot(@Nonnull Plot plot, final Runnable whenDone, @Nullable QueueCoordinator queue) { + @Override public boolean clearPlot(@Nonnull Plot plot, final Runnable whenDone, @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { PlotSquared.platform().getSetupUtils().unload(plot.getWorldName(), false); final File worldFolder = new File(PlotSquared.platform().getWorldContainer(), plot.getWorldName()); TaskManager.getPlatformImplementation().taskAsync(() -> { @@ -95,8 +96,11 @@ public class SinglePlotManager extends PlotManager { return new String[0]; } - @Override - public boolean setComponent(@Nonnull PlotId plotId, @Nonnull String component, @Nonnull Pattern blocks, @Nullable QueueCoordinator queue) { + @Override public boolean setComponent(@Nonnull PlotId plotId, + @Nonnull String component, + @Nonnull Pattern blocks, + @Nullable PlotPlayer actor, + @Nullable QueueCoordinator queue) { return false; } diff --git a/Core/src/main/java/com/plotsquared/core/queue/BasicQueueCoordinator.java b/Core/src/main/java/com/plotsquared/core/queue/BasicQueueCoordinator.java index aba4f7f08..7b57146fa 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/BasicQueueCoordinator.java +++ b/Core/src/main/java/com/plotsquared/core/queue/BasicQueueCoordinator.java @@ -25,6 +25,8 @@ */ package com.plotsquared.core.queue; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.plotsquared.core.util.PatternUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.entity.Entity; @@ -54,6 +56,7 @@ public abstract class BasicQueueCoordinator extends QueueCoordinator { private final World world; private final ConcurrentHashMap blockChunks = new ConcurrentHashMap<>(); private final List readRegion = new ArrayList<>(); + private final List progressSubscribers = new ArrayList<>(); private long modified; private LocalChunk lastWrappedChunk; private int lastX = Integer.MIN_VALUE; @@ -67,6 +70,7 @@ public abstract class BasicQueueCoordinator extends QueueCoordinator { private Consumer consumer = null; private boolean unloadAfter = true; private Runnable whenDone; + @Nullable private LightingMode lightingMode = LightingMode.valueOf(Settings.QUEUE.LIGHTING_MODE); public BasicQueueCoordinator(@Nonnull World world) { super(world); @@ -252,6 +256,28 @@ public abstract class BasicQueueCoordinator extends QueueCoordinator { this.consumer = consumer; } + /** + * Get the list of progress subscribers currently added to the queue to be added to the Chunk Coordinator + */ + public final List getProgressSubscribers() { + return this.progressSubscribers; + } + + @Override public final void addProgressSubscriber(@Nonnull ProgressSubscriber progressSubscriber) { + this.progressSubscribers.add(progressSubscriber); + } + + @Override @Nonnull public final LightingMode getLightingMode() { + if (lightingMode == null) { + return LightingMode.valueOf(Settings.QUEUE.LIGHTING_MODE); + } + return this.lightingMode; + } + + @Override public final void setLightingMode(@Nullable LightingMode mode) { + this.lightingMode = mode; + } + @Override public Runnable getCompleteTask() { return this.whenDone; } diff --git a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinator.java b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinator.java index 65bd88eea..9eeacf82b 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinator.java +++ b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinator.java @@ -29,14 +29,16 @@ import com.plotsquared.core.util.task.PlotSquaredTask; public abstract class ChunkCoordinator implements PlotSquaredTask { + private boolean cancelled = false; + @Override public abstract void runTask(); @Override public boolean isCancelled() { - return false; + return cancelled; } @Override public void cancel() { - // Do nothing + this.cancelled = true; } /** diff --git a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java index 44c2f9a45..0899527e8 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java +++ b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java @@ -27,8 +27,10 @@ package com.plotsquared.core.queue; import com.google.common.base.Preconditions; import com.google.inject.Inject; +import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.inject.factory.ChunkCoordinatorFactory; import com.plotsquared.core.location.Location; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.world.World; @@ -46,14 +48,15 @@ import java.util.function.Consumer; public class ChunkCoordinatorBuilder { private final List requestedChunks = new LinkedList<>(); + private final List progressSubscribers = new ArrayList<>(); private final ChunkCoordinatorFactory chunkCoordinatorFactory; private Consumer throwableConsumer = Throwable::printStackTrace; private World world; private Consumer chunkConsumer; private Runnable whenDone = () -> { }; - private long maxIterationTime = 60; // A little over 1 tick; - private int initialBatchSize = 4; + private long maxIterationTime = Settings.QUEUE.MAX_ITERATION_TIME; // A little over 1 tick; + private int initialBatchSize = Settings.QUEUE.INITIAL_BATCH_SIZE; private boolean unloadAfter = true; @Inject public ChunkCoordinatorBuilder(@Nonnull ChunkCoordinatorFactory chunkCoordinatorFactory) { @@ -193,6 +196,16 @@ public class ChunkCoordinatorBuilder { return this; } + @Nonnull public ChunkCoordinatorBuilder withProgressSubscriber(ProgressSubscriber progressSubscriber) { + this.progressSubscribers.add(progressSubscriber); + return this; + } + + @Nonnull public ChunkCoordinatorBuilder withProgressSubscribers(Collection progressSubscribers) { + this.progressSubscribers.addAll(progressSubscribers); + return this; + } + /** * Create a new {@link ChunkCoordinator} instance based on the values in the Builder instance. * @@ -205,7 +218,7 @@ public class ChunkCoordinatorBuilder { Preconditions.checkNotNull(this.throwableConsumer, "No throwable consumer was supplied"); return chunkCoordinatorFactory .create(this.maxIterationTime, this.initialBatchSize, this.chunkConsumer, this.world, this.requestedChunks, this.whenDone, - this.throwableConsumer, this.unloadAfter); + this.throwableConsumer, this.unloadAfter, this.progressSubscribers); } } diff --git a/Core/src/main/java/com/plotsquared/core/queue/DelegateQueueCoordinator.java b/Core/src/main/java/com/plotsquared/core/queue/DelegateQueueCoordinator.java index 826990294..8783d494f 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/DelegateQueueCoordinator.java +++ b/Core/src/main/java/com/plotsquared/core/queue/DelegateQueueCoordinator.java @@ -25,6 +25,8 @@ */ package com.plotsquared.core.queue; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.function.pattern.Pattern; @@ -190,7 +192,7 @@ public class DelegateQueueCoordinator extends QueueCoordinator { } } - @Nullable @Override public Consumer getChunkConsumer() { + @Override @Nullable public Consumer getChunkConsumer() { if (parent != null) { return parent.getChunkConsumer(); } @@ -203,6 +205,25 @@ public class DelegateQueueCoordinator extends QueueCoordinator { } } + @Override public void addProgressSubscriber(@Nonnull ProgressSubscriber progressSubscriber) { + if (parent != null) { + parent.addProgressSubscriber(progressSubscriber); + } + } + + @Override @Nonnull public LightingMode getLightingMode() { + if (parent != null) { + return parent.getLightingMode(); + } + return LightingMode.valueOf(Settings.QUEUE.LIGHTING_MODE); + } + + @Override public void setLightingMode(@Nullable LightingMode mode) { + if (parent != null) { + parent.setLightingMode(mode); + } + } + @Override @Nonnull public List getReadChunks() { if (parent != null) { return parent.getReadChunks(); @@ -246,6 +267,5 @@ public class DelegateQueueCoordinator extends QueueCoordinator { if (parent != null) { parent.setRegenRegion(regenRegion); } - } } diff --git a/Core/src/main/java/com/plotsquared/core/queue/LightingMode.java b/Core/src/main/java/com/plotsquared/core/queue/LightingMode.java new file mode 100644 index 000000000..fac5f7801 --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/queue/LightingMode.java @@ -0,0 +1,56 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.queue; + +import java.util.HashMap; +import java.util.Map; + +public enum LightingMode { + + NONE(0), PLACEMENT(1), REPLACEMENT(2), ALL(3); + + private static final Map map = new HashMap<>(); + + static { + for (LightingMode mode : LightingMode.values()) { + map.put(mode.mode, mode); + } + } + + private final int mode; + + LightingMode(int mode) { + this.mode = mode; + } + + public static LightingMode valueOf(int mode) { + return map.get(mode); + } + + public int getMode() { + return mode; + } +} diff --git a/Core/src/main/java/com/plotsquared/core/queue/QueueCoordinator.java b/Core/src/main/java/com/plotsquared/core/queue/QueueCoordinator.java index e12ec0e38..3580a9dbe 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/QueueCoordinator.java +++ b/Core/src/main/java/com/plotsquared/core/queue/QueueCoordinator.java @@ -28,6 +28,7 @@ package com.plotsquared.core.queue; import com.google.inject.Inject; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.location.Location; +import com.plotsquared.core.queue.subscriber.ProgressSubscriber; import com.plotsquared.core.util.PatternUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.entity.Entity; @@ -353,6 +354,23 @@ public abstract class QueueCoordinator { */ public abstract void setChunkConsumer(@Nonnull Consumer consumer); + /** + * Add a {@link ProgressSubscriber} to the Queue to subscribe to the relevant Chunk Processor + */ + public abstract void addProgressSubscriber(@Nonnull ProgressSubscriber progressSubscriber); + + /** + * Get the {@link LightingMode} to be used when setting blocks + */ + @Nonnull public abstract LightingMode getLightingMode(); + + /** + * Set the {@link LightingMode} to be used when setting blocks + * + * @param mode lighting mode. Null to use default. + */ + public abstract void setLightingMode(@Nullable LightingMode mode); + /** * Fill a cuboid between two positions with a BlockState * diff --git a/Core/src/main/java/com/plotsquared/core/queue/subscriber/DefaultProgressSubscriber.java b/Core/src/main/java/com/plotsquared/core/queue/subscriber/DefaultProgressSubscriber.java new file mode 100644 index 000000000..64787ae2e --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/queue/subscriber/DefaultProgressSubscriber.java @@ -0,0 +1,124 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.queue.subscriber; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.AtomicDouble; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.configuration.caption.Caption; +import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.player.PlotPlayer; +import com.plotsquared.core.queue.ChunkCoordinator; +import com.plotsquared.core.util.task.PlotSquaredTask; +import com.plotsquared.core.util.task.TaskManager; +import com.plotsquared.core.util.task.TaskTime; +import net.kyori.adventure.text.minimessage.Template; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The default PlotSquared Progress Subscriber. Can be used for both console and player tasks. + * It is the {@link ProgressSubscriber} returned by {@link com.plotsquared.core.inject.factory.ProgressSubscriberFactory}. + * Runs a repeating synchronous task notifying the given actor about any updates, saving updates notified by the ChunkCoordinator. + */ +public class DefaultProgressSubscriber implements ProgressSubscriber { + + @Nonnull private final AtomicDouble progress = new AtomicDouble(0); + @Nonnull private final AtomicBoolean started = new AtomicBoolean(false); + @Nonnull private final AtomicBoolean cancelled = new AtomicBoolean(false); + @Nonnull private final TaskTime interval; + @Nonnull private final TaskTime wait; + @Nonnull private final PlotPlayer actor; + @Nonnull private final Caption caption; + private PlotSquaredTask task; + + @AssistedInject + public DefaultProgressSubscriber() { + throw new UnsupportedOperationException("DefaultProgressSubscriber cannot be used without an actor."); + } + + @AssistedInject + public DefaultProgressSubscriber(@Nullable @Assisted("subscriber") final PlotPlayer actor) { + Preconditions.checkNotNull(actor, + "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!"); + this.actor = actor; + this.interval = TaskTime.ms(Settings.QUEUE.NOTIFY_INTERVAL); + this.wait = TaskTime.ms(Settings.QUEUE.NOTIFY_WAIT); + this.caption = TranslatableCaption.of("working.progress"); + } + + @AssistedInject + public DefaultProgressSubscriber(@Nullable @Assisted("subscriber") final PlotPlayer actor, + @Assisted("progressInterval") final long interval, + @Assisted("waitBeforeStarting") final long wait, + @Nullable @Assisted("caption") final Caption caption) { + Preconditions.checkNotNull(actor, + "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!"); + this.actor = actor; + this.interval = TaskTime.ms(interval); + this.wait = TaskTime.ms(wait); + if (caption == null) { + this.caption = TranslatableCaption.of("working.progress"); + } else { + this.caption = caption; + } + } + + @Override public void notifyProgress(@Nonnull ChunkCoordinator coordinator, double progress) { + this.progress.set(progress); + if (coordinator.isCancelled() || progress >= 1) { + if (task != null) { + task.cancel(); + } + } else if (started.compareAndSet(false, true)) { + TaskManager.getPlatformImplementation().taskLater(() -> task = TaskManager.getPlatformImplementation().taskRepeat(() -> { + if (!started.get()) { + return; + } + if (cancelled.get()) { + task.cancel(); + } + actor.sendMessage(caption, Template.of("progress", String.format("%.2f", this.progress.doubleValue() * 100))); + }, interval), wait); + } + } + + public void notifyEnd() { + cancel(); + } + + public void cancel() { + this.cancelled.set(true); + if (this.task != null) { + task.cancel(); + } + } +} diff --git a/Core/src/main/java/com/plotsquared/core/queue/subscriber/ProgressSubscriber.java b/Core/src/main/java/com/plotsquared/core/queue/subscriber/ProgressSubscriber.java new file mode 100644 index 000000000..fddda2d8d --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/queue/subscriber/ProgressSubscriber.java @@ -0,0 +1,46 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2020 IntellectualSites + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.plotsquared.core.queue.subscriber; + +import com.plotsquared.core.queue.ChunkCoordinator; + +import javax.annotation.Nonnull; + +public interface ProgressSubscriber { + + /** + * Notify about a progress update in the coordinator + * + * @param coordinator Coordinator instance that triggered the notification + * @param progress Progress in the range [0, 1] + */ + void notifyProgress(@Nonnull final ChunkCoordinator coordinator, final double progress); + + /** + * Notify the subscriber that its parent ChunkCoordinator has finished + */ + void notifyEnd(); +} 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 20f5361b3..e406c10d6 100644 --- a/Core/src/main/java/com/plotsquared/core/util/RegionManager.java +++ b/Core/src/main/java/com/plotsquared/core/util/RegionManager.java @@ -27,7 +27,11 @@ package com.plotsquared.core.util; import com.google.inject.Inject; import com.plotsquared.core.PlotSquared; +import com.plotsquared.core.configuration.Settings; +import com.plotsquared.core.configuration.caption.TranslatableCaption; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.PlotManager; @@ -59,10 +63,13 @@ public abstract class RegionManager { public static RegionManager manager = null; private final WorldUtil worldUtil; private final GlobalBlockQueue blockQueue; + private final ProgressSubscriberFactory subscriberFactory; - @Inject public RegionManager(@Nonnull WorldUtil worldUtil, @Nonnull GlobalBlockQueue blockQueue) { + @Inject + public RegionManager(@Nonnull WorldUtil worldUtil, @Nonnull GlobalBlockQueue blockQueue, @Nonnull ProgressSubscriberFactory subscriberFactory) { this.worldUtil = worldUtil; this.blockQueue = blockQueue; + this.subscriberFactory = subscriberFactory; } public static BlockVector2 getRegion(Location location) { @@ -106,20 +113,25 @@ public abstract class RegionManager { * @param blocks pattern * @param minY y to set from * @param maxY y to set to + * @param actor the actor associated with the cuboid set * @param queue Nullable {@link QueueCoordinator}. If null, creates own queue and enqueues, * otherwise writes to the queue but does not enqueue. * @return true if not enqueued, otherwise whether the created queue enqueued. */ - public boolean setCuboids(final PlotArea area, - final Set regions, - final Pattern blocks, + public boolean setCuboids(@Nonnull final PlotArea area, + @Nonnull final Set regions, + @Nonnull final Pattern blocks, int minY, int maxY, + @Nullable PlotPlayer actor, @Nullable QueueCoordinator queue) { boolean enqueue = false; if (queue == null) { queue = area.getQueue(); enqueue = true; + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } } for (CuboidRegion region : regions) { Location pos1 = Location.at(area.getWorldName(), region.getMinimumPoint().getX(), minY, region.getMinimumPoint().getZ()); @@ -145,9 +157,13 @@ public abstract class RegionManager { * @param plot plot * @param whenDone task to run when complete * @param manager plot manager + * @param actor the player running the clear * @return true if the clear worked. False if someone went wrong so P2 can then handle the clear */ - public abstract boolean handleClear(Plot plot, final Runnable whenDone, PlotManager manager); + public abstract boolean handleClear(@Nonnull Plot plot, + @Nullable final Runnable whenDone, + @Nonnull PlotManager manager, + @Nullable PlotPlayer actor); /** * Copy a region to a new location (in the same world) @@ -155,10 +171,15 @@ public abstract class RegionManager { * @param pos1 position 1 * @param pos2 position 2 * @param newPos position to move pos1 to + * @param actor the actor associated with the region copy * @param whenDone task to run when complete * @return success or not */ - public boolean copyRegion(final Location pos1, final Location pos2, final Location newPos, final Runnable whenDone) { + public boolean copyRegion(@Nonnull final Location pos1, + @Nonnull final Location pos2, + @Nonnull final Location newPos, + @Nullable final PlotPlayer actor, + @Nonnull final Runnable whenDone) { final int relX = newPos.getX() - pos1.getX(); final int relZ = newPos.getZ() - pos1.getZ(); final com.sk89q.worldedit.world.World oldWorld = worldUtil.getWeWorld(pos1.getWorldName()); @@ -167,9 +188,17 @@ public abstract class RegionManager { final BasicQueueCoordinator copyTo = (BasicQueueCoordinator) blockQueue.getNewQueue(newWorld); copyFromTo(pos1, pos2, relX, relZ, oldWorld, copyFrom, copyTo, false); copyFrom.setCompleteTask(copyTo::enqueue); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + copyFrom.addProgressSubscriber(subscriberFactory + .createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, TranslatableCaption.of("swap.progress_region_copy"))); + } copyFrom .addReadChunks(new CuboidRegion(BlockVector3.at(pos1.getX(), 0, pos1.getZ()), BlockVector3.at(pos2.getX(), 0, pos2.getZ())).getChunks()); copyTo.setCompleteTask(whenDone); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + copyTo.addProgressSubscriber(subscriberFactory + .createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, TranslatableCaption.of("swap.progress_region_paste"))); + } return copyFrom.enqueue(); } @@ -188,7 +217,16 @@ public abstract class RegionManager { public abstract void clearAllEntities(Location pos1, Location pos2); - public void swap(Location pos1, Location pos2, Location swapPos, final Runnable whenDone) { + /** + * Swap two regions withn the same world + * + * @param pos1 position 1 + * @param pos2 position 2 + * @param swapPos position to swap with + * @param actor the actor associated with the region copy + * @param whenDone task to run when complete + */ + public void swap(Location pos1, Location pos2, Location swapPos, @Nullable final PlotPlayer actor, final Runnable whenDone) { int relX = swapPos.getX() - pos1.getX(); int relZ = swapPos.getZ() - pos1.getZ(); @@ -208,9 +246,26 @@ public abstract class RegionManager { copyFromTo(pos1, pos2, relX, relZ, world1, fromQueue1, toQueue2, true); copyFromTo(pos1, pos2, relX, relZ, world1, fromQueue2, toQueue1, true); fromQueue1.setCompleteTask(fromQueue2::enqueue); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + fromQueue1.addProgressSubscriber(subscriberFactory + .createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, TranslatableCaption.of("swap.progress_region1_copy"))); + } fromQueue2.setCompleteTask(toQueue1::enqueue); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + fromQueue2.addProgressSubscriber(subscriberFactory + .createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, TranslatableCaption.of("swap.progress_region2_copy"))); + } toQueue1.setCompleteTask(toQueue2::enqueue); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + toQueue1.addProgressSubscriber(subscriberFactory.createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, + TranslatableCaption.of("swap.progress_region1_paste"))); + } toQueue2.setCompleteTask(whenDone); + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + toQueue2.addProgressSubscriber(subscriberFactory.createFull(actor, Settings.QUEUE.NOTIFY_INTERVAL, Settings.QUEUE.NOTIFY_WAIT, + TranslatableCaption.of("swap.progress_region2_paste"))); + } + fromQueue1.enqueue(); } private void copyFromTo(Location pos1, diff --git a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java index 13e1dfa94..bc9581ea4 100644 --- a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java +++ b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java @@ -25,13 +25,16 @@ */ package com.plotsquared.core.util; +import com.google.inject.Inject; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonParseException; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.generator.ClassicPlotWorld; +import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; import com.plotsquared.core.location.Location; +import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.plot.schematic.Schematic; @@ -111,9 +114,11 @@ public abstract class SchematicHandler { public static SchematicHandler manager; private final WorldUtil worldUtil; private boolean exportAll = false; + private final ProgressSubscriberFactory subscriberFactory; - public SchematicHandler(@Nonnull final WorldUtil worldUtil) { + @Inject public SchematicHandler(@Nonnull final WorldUtil worldUtil, @Nonnull ProgressSubscriberFactory subscriberFactory) { this.worldUtil = worldUtil; + this.subscriberFactory = subscriberFactory; } public static void upload(@Nullable UUID uuid, @@ -259,6 +264,7 @@ public abstract class SchematicHandler { * @param yOffset offset y to paste it from plot origin * @param zOffset offset z to paste it from plot origin * @param autoHeight if to automatically choose height to paste from + * @param actor the actor pasting the schematic * @param whenDone task to run when schematic is pasted */ public void paste(final Schematic schematic, @@ -267,6 +273,7 @@ public abstract class SchematicHandler { final int yOffset, final int zOffset, final boolean autoHeight, + final PlotPlayer actor, final RunnableVal whenDone) { TaskManager.runTask(() -> { @@ -346,6 +353,9 @@ public abstract class SchematicHandler { if (whenDone != null) { whenDone.value = true; } + if (actor != null && Settings.QUEUE.NOTIFY_PROGRESS) { + queue.addProgressSubscriber(subscriberFactory.createWithActor(actor)); + } queue.setCompleteTask(whenDone); queue.enqueue(); } catch (Exception e) { diff --git a/Core/src/main/java/com/plotsquared/core/util/task/AutoClaimFinishTask.java b/Core/src/main/java/com/plotsquared/core/util/task/AutoClaimFinishTask.java index 7dac88333..ded5f1c35 100644 --- a/Core/src/main/java/com/plotsquared/core/util/task/AutoClaimFinishTask.java +++ b/Core/src/main/java/com/plotsquared/core/util/task/AutoClaimFinishTask.java @@ -41,13 +41,13 @@ import java.util.concurrent.Callable; public final class AutoClaimFinishTask implements Callable { - private final PlotPlayer player; + private final PlotPlayer player; private final Plot plot; private final PlotArea area; private final String schematic; private final EventDispatcher eventDispatcher; - public AutoClaimFinishTask(final PlotPlayer player, final Plot plot, final PlotArea area, + public AutoClaimFinishTask(final PlotPlayer player, final Plot plot, final PlotArea area, final String schematic, final EventDispatcher eventDispatcher) { this.player = player; this.plot = plot; @@ -72,7 +72,7 @@ public final class AutoClaimFinishTask implements Callable { player.sendMessage(TranslatableCaption.of("events.event_denied"), Templates.of("value", "Auto Merge")); } else { - plot.getPlotModificationManager().autoMerge(event.getDir(), event.getMax(), player.getUUID(), true); + plot.getPlotModificationManager().autoMerge(event.getDir(), event.getMax(), player.getUUID(), player, true); } } return true; diff --git a/Core/src/main/resources/lang/messages_en.json b/Core/src/main/resources/lang/messages_en.json index 7ea0fa700..aea7b8756 100644 --- a/Core/src/main/resources/lang/messages_en.json +++ b/Core/src/main/resources/lang/messages_en.json @@ -74,6 +74,12 @@ "swap.swap_overlap": "The proposed areas are not allowed to overlap.", "swap.swap_success": "Successfully swapped plots.", "swap.swap_merged": "Merged plots may not be swapped. Please unmerge the plots before performing the swap.", + "swap.progress_region1_copy": "Current region 1 copy progress: %", + "swap.progress_region2_copy": "Current region 2 copy progress: %Current region 1 paste progress: %", + "swap.progress_region2_paste": "Current region 2 paste progress: %", + "swap.progress_region_copy": "Current copy progress: %", + "swap.progress_region_paste": "Current paste progress: %", "comment.inbox_notification": " unread messages. Use .", "comment.not_valid_inbox_index": "No comment at index .", @@ -411,6 +417,8 @@ "working.plot_not_claimed": "Plot not claimed.", "working.plot_is_claimed": "This plot is already claimed.", "working.claimed": "You successfully claimed the plot.", + "working.progress": "Current progress: %", + "working.component_complete": "Component generation has finished.", "list.comment_list_header_paged": "(Page /) List of comments", "list.comment_list_comment": "[#[;][]\n",