PlotSquared/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitChunkCoordinator.java

282 lines
10 KiB
Java
Raw Normal View History

2020-07-12 14:43:57 +02:00
/*
* PlotSquared, a land and world management plugin for Minecraft.
* Copyright (C) IntellectualSites <https://intellectualsites.com>
* Copyright (C) IntellectualSites team and contributors
2020-07-12 14:43:57 +02:00
*
* 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.
2020-07-12 14:43:57 +02:00
*
* 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.
2020-07-12 14:43:57 +02:00
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
2020-07-12 14:43:57 +02:00
*/
package com.plotsquared.bukkit.queue;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.plotsquared.bukkit.BukkitPlatform;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.queue.ChunkCoordinator;
2020-08-17 17:21:11 +02:00
import com.plotsquared.core.queue.subscriber.ProgressSubscriber;
import com.plotsquared.core.util.task.PlotSquaredTask;
import com.plotsquared.core.util.task.TaskManager;
import com.plotsquared.core.util.task.TaskTime;
2020-07-12 14:43:57 +02:00
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.world.World;
2020-07-12 14:43:57 +02:00
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
2020-07-12 14:43:57 +02:00
import org.bukkit.Chunk;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.checkerframework.checker.nullness.qual.NonNull;
2020-07-12 14:43:57 +02:00
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
* <p>
* 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
2020-08-16 12:34:08 +02:00
* </p>
**/
public final class BukkitChunkCoordinator extends ChunkCoordinator {
2020-07-12 14:43:57 +02:00
2020-07-17 15:00:01 +02:00
private final List<ProgressSubscriber> progressSubscribers = new LinkedList<>();
2020-07-12 14:43:57 +02:00
private final Queue<BlockVector2> requestedChunks;
private final Queue<Chunk> availableChunks;
private final long maxIterationTime;
private final Plugin plugin;
private final Consumer<BlockVector2> chunkConsumer;
private final org.bukkit.World bukkitWorld;
2020-07-12 14:43:57 +02:00
private final Runnable whenDone;
2020-07-17 15:00:01 +02:00
private final Consumer<Throwable> throwableConsumer;
private final boolean unloadAfter;
2020-07-17 15:00:01 +02:00
private final int totalSize;
private final AtomicInteger expectedSize;
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
private final AtomicInteger loadingChunks = new AtomicInteger();
private final boolean forceSync;
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
2020-07-12 14:43:57 +02:00
private int batchSize;
private PlotSquaredTask task;
private volatile boolean shouldCancel;
private boolean finished;
2020-07-12 14:43:57 +02:00
@Inject
private BukkitChunkCoordinator(
@Assisted final long maxIterationTime,
@Assisted final int initialBatchSize,
@Assisted final @NonNull Consumer<BlockVector2> chunkConsumer,
@Assisted final @NonNull World world,
@Assisted final @NonNull Collection<BlockVector2> requestedChunks,
@Assisted final @NonNull Runnable whenDone,
@Assisted final @NonNull Consumer<Throwable> throwableConsumer,
@Assisted("unloadAfter") final boolean unloadAfter,
@Assisted final @NonNull Collection<ProgressSubscriber> progressSubscribers,
@Assisted("forceSync") final boolean forceSync
) {
2020-07-12 14:43:57 +02:00
this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks);
this.availableChunks = new LinkedBlockingQueue<>();
2020-07-17 15:00:01 +02:00
this.totalSize = requestedChunks.size();
this.expectedSize = new AtomicInteger(this.totalSize);
2020-07-12 14:43:57 +02:00
this.batchSize = initialBatchSize;
this.chunkConsumer = chunkConsumer;
this.maxIterationTime = maxIterationTime;
this.whenDone = whenDone;
2020-07-17 15:00:01 +02:00
this.throwableConsumer = throwableConsumer;
this.unloadAfter = unloadAfter;
this.plugin = JavaPlugin.getPlugin(BukkitPlatform.class);
this.bukkitWorld = Bukkit.getWorld(world.getName());
2020-08-17 17:21:11 +02:00
this.progressSubscribers.addAll(progressSubscribers);
this.forceSync = forceSync;
}
@Override
public void start() {
if (!forceSync) {
// Request initial batch
this.requestBatch();
// Wait until next tick to give the chunks a chance to be loaded
TaskManager.runTaskLater(() -> task = TaskManager.runTaskRepeat(this, TaskTime.ticks(1)), TaskTime.ticks(1));
} else {
try {
while (!shouldCancel && !requestedChunks.isEmpty()) {
chunkConsumer.accept(requestedChunks.poll());
}
} catch (Throwable t) {
throwableConsumer.accept(t);
} finally {
finish();
}
}
2020-07-17 15:00:01 +02:00
}
@Override
public void cancel() {
shouldCancel = true;
}
private void finish() {
try {
this.whenDone.run();
} catch (final Throwable throwable) {
this.throwableConsumer.accept(throwable);
} finally {
for (final ProgressSubscriber subscriber : this.progressSubscribers) {
subscriber.notifyEnd();
}
if (task != null) {
task.cancel();
}
finished = true;
}
}
@Override
public void run() {
if (shouldCancel) {
if (unloadAfter) {
Chunk chunk;
while ((chunk = availableChunks.poll()) != null) {
freeChunk(chunk);
}
}
finish();
return;
}
2020-07-12 14:43:57 +02:00
Chunk chunk = this.availableChunks.poll();
if (chunk == null) {
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
if (this.availableChunks.isEmpty()) {
if (this.requestedChunks.isEmpty() && loadingChunks.get() == 0) {
finish();
} else {
requestBatch();
}
}
2020-07-12 14:43:57 +02:00
return;
}
2020-09-11 13:59:40 +02:00
long[] iterationTime = new long[2];
2020-07-12 14:43:57 +02:00
int processedChunks = 0;
do {
final long start = System.currentTimeMillis();
try {
this.chunkConsumer.accept(BlockVector2.at(chunk.getX(), chunk.getZ()));
2020-07-17 15:00:01 +02:00
} catch (final Throwable throwable) {
this.throwableConsumer.accept(throwable);
2020-07-12 14:43:57 +02:00
}
if (unloadAfter) {
this.freeChunk(chunk);
}
2020-07-12 14:43:57 +02:00
processedChunks++;
final long end = System.currentTimeMillis();
// Update iteration time
2020-09-11 13:59:40 +02:00
iterationTime[0] = iterationTime[1];
iterationTime[1] = end - start;
} while (iterationTime[0] + iterationTime[1] < this.maxIterationTime * 2 && (chunk = availableChunks.poll()) != null);
2020-07-12 14:43:57 +02:00
if (processedChunks < this.batchSize) {
// Adjust batch size based on the amount of processed chunks per tick
this.batchSize = processedChunks;
}
2020-07-17 15:00:01 +02:00
final int expected = this.expectedSize.addAndGet(-processedChunks);
if (expected <= 0) {
finish();
2020-07-12 14:43:57 +02:00
} 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);
}
2020-07-12 14:43:57 +02:00
this.requestBatch();
}
}
}
2020-07-24 17:24:53 +02:00
/**
* Requests a batch of chunks to be loaded
*/
2020-07-12 14:43:57 +02:00
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
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
loadingChunks.incrementAndGet();
PaperLib
.getChunkAtAsync(this.bukkitWorld, chunk.getX(), chunk.getZ(), true, true)
.whenComplete((chunkObject, throwable) -> {
Implement extended world heights from Y-64 to Y319 #3473 (#3473) * Begin to implement extended world heights: - Implemented in Bukkit module (and where required in Core module) * Implement extended world heights into core module * Add min gen height to setup, * Default gen/build heights based on minecraft version * Few fixes * Fix up queues * Address comments * Make road schematic stuff slightly more efficient by sharing queues * Minor fixes, don't overlay error many times for the same y * Fix incorrect schematic paste height, undo changes to HybridUtils * Overhall regenallroads method to make it work, make sure BukkitChunkCoordinator can/will finish * Process chunks in order when regenerating all roads * Address comments * Address comments * Ground level//bedrock is at min gen height - Add comment on == rather than <= being used - It's because it's only checking for the bedrock layer being broken if that's disabled * Fix offset for min build height in SchematicHandler * Better javadoc Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com> * Address inclusivity issues for max world height * Javadocs/comments/deprecation * Use world min/max heights if present in QueueCoordinator * Address some deprecations for regions and biome setting * Add a count for chunks we're currently trying to load to not skip chunks at the end of a queue's edit * Use minGenHeight + 1 rather than build height in AugmentedUtils * Create utility method for layer index in GenChunk * Correct height in HybridUtils, also use minGenHeight + 1 * Don't magically split to 128 height in regeneration * Add utility methods for world height in QueueCoordinator * Clean up ClassicPlotManager road creation/removal * Start generation at min gen height if bedrock is disabled * min gen height is set in PlotArea * Add note on schem y normalisation * Improve plot getVolume method readability * Don't overly extend height when regenerating road region * y index utility method in ChunknQueueCoordinator * Layer index utility method in LocalChunk * Use version min/max heights if world not present in QueueCoordinator * Fix min -> max * Don't allow players to modify outside build height when using plot set / schematics. - Also fixes schematic height issues * Remove debug * Address comments * Switch loadingChunks to AtomicInteger to be safe (in case of multi-threaded) * Fix "security" issue that was already present * Ensure sign isn't physicsed Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
2022-03-05 19:03:39 +01:00
loadingChunks.decrementAndGet();
if (throwable != null) {
throwable.printStackTrace();
// We want one less because this couldn't be processed
this.expectedSize.decrementAndGet();
} else if (PlotSquared.get().isMainThread(Thread.currentThread())) {
this.processChunk(chunkObject);
} else {
TaskManager.runTask(() -> this.processChunk(chunkObject));
}
});
2020-07-12 14:43:57 +02:00
}
}
2020-07-24 17:24:53 +02:00
/**
* Once a chunk has been loaded, process it (add a plugin ticket and add to
* available chunks list). It is important that this gets executed on the
* server's main thread.
2020-07-24 17:24:53 +02:00
*/
private void processChunk(final @NonNull Chunk chunk) {
2020-07-12 14:43:57 +02:00
if (!chunk.isLoaded()) {
2020-07-24 17:24:53 +02:00
throw new IllegalArgumentException(String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ()));
2020-07-12 14:43:57 +02:00
}
if (finished) {
return;
}
2020-07-12 14:43:57 +02:00
chunk.addPluginChunkTicket(this.plugin);
this.availableChunks.add(chunk);
}
2020-07-24 17:24:53 +02:00
/**
* Once a chunk has been used, free it up for unload by removing the plugin ticket
*/
private void freeChunk(final @NonNull Chunk chunk) {
2020-07-12 14:43:57 +02:00
if (!chunk.isLoaded()) {
2020-07-24 17:24:53 +02:00
throw new IllegalArgumentException(String.format("Chunk %d;%d is is not loaded", chunk.getX(), chunk.getZ()));
2020-07-12 14:43:57 +02:00
}
chunk.removePluginChunkTicket(this.plugin);
}
@Override
public int getRemainingChunks() {
2020-07-17 15:00:01 +02:00
return this.expectedSize.get();
}
@Override
public int getTotalChunks() {
2020-07-17 15:00:01 +02:00
return this.totalSize;
}
/**
* Subscribe to coordinator progress updates
*
* @param subscriber Subscriber
*/
public void subscribeToProgress(final @NonNull ProgressSubscriber subscriber) {
2020-07-17 15:00:01 +02:00
this.progressSubscribers.add(subscriber);
}
2020-07-12 14:43:57 +02:00
}