mirror of
https://github.com/IntellectualSites/PlotSquared.git
synced 2024-12-28 17:38:01 +01:00
begin new block setting/chunk pipeline
This will ultimately replace both the GlobalBlockQueue and the ChunkTask stuff
This commit is contained in:
parent
8eb903ad72
commit
def9a1bcf8
@ -26,7 +26,7 @@
|
||||
package com.plotsquared.bukkit.queue;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.plotsquared.bukkit.BukkitMain;
|
||||
import com.plotsquared.bukkit.BukkitPlatform;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Chunk;
|
||||
@ -44,34 +44,7 @@ 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
|
||||
* <p>
|
||||
* Usage:
|
||||
* <pre>{@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();
|
||||
* }</pre>
|
||||
*
|
||||
* @author Alexander Söderberg
|
||||
* @see #builder() To create a new coordinator instance
|
||||
*/
|
||||
public final class ChunkCoordinator extends BukkitRunnable {
|
||||
|
||||
private final List<ProgressSubscriber> progressSubscribers = new LinkedList<>();
|
||||
public final class BukkitChunkCoordinator extends BukkitRunnable {
|
||||
|
||||
private final Queue<BlockVector2> requestedChunks;
|
||||
private final Queue<Chunk> availableChunks;
|
||||
@ -80,48 +53,32 @@ public final class ChunkCoordinator extends BukkitRunnable {
|
||||
private final Consumer<Chunk> chunkConsumer;
|
||||
private final World world;
|
||||
private final Runnable whenDone;
|
||||
private final Consumer<Throwable> throwableConsumer;
|
||||
private final int totalSize;
|
||||
|
||||
private AtomicInteger expectedSize;
|
||||
private int batchSize;
|
||||
|
||||
private ChunkCoordinator(final long maxIterationTime, final int initialBatchSize,
|
||||
private BukkitChunkCoordinator(final long maxIterationTime, final int initialBatchSize,
|
||||
@NotNull final Consumer<Chunk> chunkConsumer, @NotNull final World world,
|
||||
@NotNull final Collection<BlockVector2> requestedChunks, @NotNull final Runnable whenDone,
|
||||
@NotNull final Consumer<Throwable> throwableConsumer) {
|
||||
@NotNull final Collection<BlockVector2> requestedChunks, @NotNull final Runnable whenDone) {
|
||||
this.requestedChunks = new LinkedBlockingQueue<>(requestedChunks);
|
||||
this.availableChunks = new LinkedBlockingQueue<>();
|
||||
this.totalSize = requestedChunks.size();
|
||||
this.expectedSize = new AtomicInteger(this.totalSize);
|
||||
this.expectedSize = new AtomicInteger(requestedChunks.size());
|
||||
this.world = world;
|
||||
this.batchSize = initialBatchSize;
|
||||
this.chunkConsumer = chunkConsumer;
|
||||
this.maxIterationTime = maxIterationTime;
|
||||
this.whenDone = whenDone;
|
||||
this.throwableConsumer = throwableConsumer;
|
||||
this.plugin = JavaPlugin.getPlugin(BukkitMain.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ChunkCoordinator} instance
|
||||
*
|
||||
* @return Coordinator builder instance
|
||||
*/
|
||||
@NotNull public static ChunkCoordinatorBuilder builder() {
|
||||
return new ChunkCoordinatorBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the coordinator instance
|
||||
*/
|
||||
public void start() {
|
||||
this.plugin = JavaPlugin.getPlugin(BukkitPlatform.class);
|
||||
// Request initial batch
|
||||
this.requestBatch();
|
||||
// Wait until next tick to give the chunks a chance to be loaded
|
||||
this.runTaskTimer(this.plugin, 1L, 1L);
|
||||
}
|
||||
|
||||
@NotNull public static ChunkCoordinatorBuilder builder() {
|
||||
return new ChunkCoordinatorBuilder();
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
Chunk chunk = this.availableChunks.poll();
|
||||
if (chunk == null) {
|
||||
@ -133,8 +90,7 @@ public final class ChunkCoordinator extends BukkitRunnable {
|
||||
final long start = System.currentTimeMillis();
|
||||
try {
|
||||
this.chunkConsumer.accept(chunk);
|
||||
} catch (final Throwable throwable) {
|
||||
this.throwableConsumer.accept(throwable);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
this.freeChunk(chunk);
|
||||
processedChunks++;
|
||||
@ -147,19 +103,10 @@ public final class ChunkCoordinator extends BukkitRunnable {
|
||||
// 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) {
|
||||
if (this.expectedSize.addAndGet(-processedChunks) <= 0) {
|
||||
try {
|
||||
this.whenDone.run();
|
||||
} catch (final Throwable throwable) {
|
||||
this.throwableConsumer.accept(throwable);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
this.cancel();
|
||||
} else {
|
||||
@ -203,52 +150,10 @@ public final class ChunkCoordinator extends BukkitRunnable {
|
||||
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(@NotNull 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(@NotNull final ChunkCoordinator coordinator, final float progress);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static final class ChunkCoordinatorBuilder {
|
||||
|
||||
private final List<BlockVector2> requestedChunks = new LinkedList<>();
|
||||
private Consumer<Throwable> throwableConsumer = Throwable::printStackTrace;
|
||||
private World world;
|
||||
private Consumer<Chunk> chunkConsumer;
|
||||
private Runnable whenDone = () -> {
|
||||
@ -303,22 +208,12 @@ public final class ChunkCoordinator extends BukkitRunnable {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull public ChunkCoordinatorBuilder withThrowableConsumer(
|
||||
@NotNull final Consumer<Throwable> throwableConsumer) {
|
||||
this.throwableConsumer =
|
||||
Preconditions.checkNotNull(throwableConsumer, "Throwable consumer may not be null");
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull public ChunkCoordinator build() {
|
||||
@NotNull public BukkitChunkCoordinator 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);
|
||||
return new BukkitChunkCoordinator(this.maxIterationTime, this.initialBatchSize,
|
||||
this.chunkConsumer, this.world, this.requestedChunks, this.whenDone);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* _____ _ _ _____ _
|
||||
* | __ \| | | | / ____| | |
|
||||
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
|
||||
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
|
||||
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
|
||||
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
|
||||
* | |
|
||||
* |_|
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.plotsquared.bukkit.queue;
|
||||
|
||||
import com.plotsquared.bukkit.util.BukkitBlockUtil;
|
||||
import com.plotsquared.core.queue.BasicQueueCoordinator;
|
||||
import com.plotsquared.core.util.BlockUtil;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
public class BukkitQueueCoordinator extends BasicQueueCoordinator {
|
||||
|
||||
public BukkitQueueCoordinator(String world) {
|
||||
super(world);
|
||||
}
|
||||
|
||||
@Override public BlockState getBlock(int x, int y, int z) {
|
||||
World worldObj = Bukkit.getWorld(getWorld());
|
||||
if (worldObj != null) {
|
||||
Block block = worldObj.getBlockAt(x, y, z);
|
||||
return BukkitBlockUtil.get(block);
|
||||
} else {
|
||||
return BlockUtil.get(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* _____ _ _ _____ _
|
||||
* | __ \| | | | / ____| | |
|
||||
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
|
||||
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
|
||||
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
|
||||
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
|
||||
* | |
|
||||
* |_|
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.plotsquared.core.inject.annotations;
|
||||
|
||||
import com.google.inject.BindingAnnotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@BindingAnnotation
|
||||
public @interface QueuePipeline {
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* _____ _ _ _____ _
|
||||
* | __ \| | | | / ____| | |
|
||||
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
|
||||
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
|
||||
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
|
||||
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
|
||||
* | |
|
||||
* |_|
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.plotsquared.core.inject.factory;
|
||||
|
||||
import com.plotsquared.core.queue.QueueCoordinator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface QueueCoordinatorFactory {
|
||||
|
||||
@Nonnull QueueCoordinator create();
|
||||
|
||||
}
|
@ -26,22 +26,23 @@
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.plot.PlotArea;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import lombok.Getter;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class AreaBoundDelegateLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
public class AreaBoundDelegateQueueCoordinator extends DelegateQueueCoordinator {
|
||||
|
||||
@Getter private final PlotArea area;
|
||||
|
||||
public AreaBoundDelegateLocalBlockQueue(@Nonnull final PlotArea area,
|
||||
@Nullable final LocalBlockQueue parent) {
|
||||
public AreaBoundDelegateQueueCoordinator(@Nonnull final PlotArea area,
|
||||
@Nullable final QueueCoordinator parent) {
|
||||
super(parent);
|
||||
this.area = Objects.requireNonNull(area);
|
||||
}
|
||||
@ -74,4 +75,11 @@ public class AreaBoundDelegateLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
if (area.contains(x, z)) {
|
||||
return super.setTile(x, y, z, tag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,272 +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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.util.MainUtil;
|
||||
import com.plotsquared.core.util.MathMan;
|
||||
import com.plotsquared.core.util.PatternUtil;
|
||||
import com.plotsquared.core.util.task.RunnableVal;
|
||||
import com.plotsquared.core.util.task.TaskManager;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public abstract class BasicLocalBlockQueue extends LocalBlockQueue {
|
||||
|
||||
private final String world;
|
||||
private final ConcurrentHashMap<Long, LocalChunk> blockChunks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentLinkedDeque<LocalChunk> chunks = new ConcurrentLinkedDeque<>();
|
||||
private long modified;
|
||||
private LocalChunk lastWrappedChunk;
|
||||
private int lastX = Integer.MIN_VALUE;
|
||||
private int lastZ = Integer.MIN_VALUE;
|
||||
private boolean setbiome = false;
|
||||
|
||||
private GlobalBlockQueue globalBlockQueue;
|
||||
|
||||
public BasicLocalBlockQueue(String world) {
|
||||
super(world);
|
||||
this.world = world;
|
||||
this.modified = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public abstract LocalChunk getLocalChunk(int x, int z);
|
||||
|
||||
@Override public abstract BlockState getBlock(int x, int y, int z);
|
||||
|
||||
public abstract void setComponents(LocalChunk lc)
|
||||
throws ExecutionException, InterruptedException;
|
||||
|
||||
@Override public final String getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override public final boolean next() {
|
||||
lastX = Integer.MIN_VALUE;
|
||||
lastZ = Integer.MIN_VALUE;
|
||||
try {
|
||||
if (this.blockChunks.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
synchronized (blockChunks) {
|
||||
LocalChunk chunk = chunks.poll();
|
||||
if (chunk != null) {
|
||||
blockChunks.remove(chunk.longHash());
|
||||
return this.execute(chunk);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean execute(@Nonnull LocalChunk lc)
|
||||
throws ExecutionException, InterruptedException {
|
||||
this.setComponents(lc);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void startSet(boolean parallel) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override public void endSet(boolean parallel) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override public final int size() {
|
||||
return chunks.size();
|
||||
}
|
||||
|
||||
@Override public final long getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override public final void setModified(long modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, @Nonnull Pattern pattern) {
|
||||
return setBlock(x, y, z, PatternUtil.apply(pattern, x, y, z));
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BaseBlock id) {
|
||||
if ((y > 255) || (y < 0)) {
|
||||
return false;
|
||||
}
|
||||
int cx = x >> 4;
|
||||
int cz = z >> 4;
|
||||
if (cx != lastX || cz != lastZ) {
|
||||
lastX = cx;
|
||||
lastZ = cz;
|
||||
long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL;
|
||||
lastWrappedChunk = this.blockChunks.get(pair);
|
||||
if (lastWrappedChunk == null) {
|
||||
lastWrappedChunk = this.getLocalChunk(x >> 4, z >> 4);
|
||||
lastWrappedChunk.setBlock(x & 15, y, z & 15, id);
|
||||
LocalChunk previous = this.blockChunks.put(pair, lastWrappedChunk);
|
||||
if (previous == null) {
|
||||
return chunks.add(lastWrappedChunk);
|
||||
}
|
||||
this.blockChunks.put(pair, previous);
|
||||
lastWrappedChunk = previous;
|
||||
}
|
||||
}
|
||||
lastWrappedChunk.setBlock(x & 15, y, z & 15, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BlockState id) {
|
||||
// Trying to mix BlockState and BaseBlock leads to all kinds of issues.
|
||||
// Since BaseBlock has more features than BlockState, simply convert
|
||||
// all BlockStates to BaseBlocks
|
||||
return setBlock(x, y, z, id.toBaseBlock());
|
||||
}
|
||||
|
||||
@Override public final boolean setBiome(int x, int z, BiomeType biomeType) {
|
||||
long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL;
|
||||
LocalChunk result = this.blockChunks.get(pair);
|
||||
if (result == null) {
|
||||
result = this.getLocalChunk(x >> 4, z >> 4);
|
||||
LocalChunk previous = this.blockChunks.put(pair, result);
|
||||
if (previous != null) {
|
||||
this.blockChunks.put(pair, previous);
|
||||
result = previous;
|
||||
} else {
|
||||
chunks.add(result);
|
||||
}
|
||||
}
|
||||
result.setBiome(x & 15, z & 15, biomeType);
|
||||
setbiome = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public final boolean setBiome() {
|
||||
return setbiome;
|
||||
}
|
||||
|
||||
public final void setChunk(LocalChunk chunk) {
|
||||
LocalChunk previous = this.blockChunks.put(chunk.longHash(), chunk);
|
||||
if (previous != null) {
|
||||
chunks.remove(previous);
|
||||
}
|
||||
chunks.add(chunk);
|
||||
}
|
||||
|
||||
@Override public void flush() {
|
||||
this.globalBlockQueue.dequeue(this);
|
||||
TaskManager.getImplementation().sync(new RunnableVal<Object>() {
|
||||
@Override public void run(Object value) {
|
||||
while (next()) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public abstract class LocalChunk {
|
||||
public final BasicLocalBlockQueue parent;
|
||||
public final int z;
|
||||
public final int x;
|
||||
|
||||
public BaseBlock[][] baseblocks;
|
||||
public BiomeType[][] biomes;
|
||||
|
||||
public LocalChunk(BasicLocalBlockQueue parent, int x, int z) {
|
||||
this.parent = parent;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent queue this chunk belongs to
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BasicLocalBlockQueue getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public abstract void setBlock(final int x, final int y, final int z, final BaseBlock block);
|
||||
|
||||
public void setBiome(int x, int z, BiomeType biomeType) {
|
||||
if (this.biomes == null) {
|
||||
this.biomes = new BiomeType[16][];
|
||||
}
|
||||
BiomeType[] index = this.biomes[x];
|
||||
if (index == null) {
|
||||
index = this.biomes[x] = new BiomeType[16];
|
||||
}
|
||||
index[z] = biomeType;
|
||||
}
|
||||
|
||||
public long longHash() {
|
||||
return MathMan.pairInt(x, z);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return MathMan.pair((short) x, (short) z);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class BasicLocalChunk extends LocalChunk {
|
||||
|
||||
public BasicLocalChunk(BasicLocalBlockQueue parent, int x, int z) {
|
||||
super(parent, x, z);
|
||||
baseblocks = new BaseBlock[16][];
|
||||
}
|
||||
|
||||
@Override public void setBlock(int x, int y, int z, BaseBlock block) {
|
||||
this.setInternal(x, y, z, block);
|
||||
}
|
||||
|
||||
private void setInternal(final int x, final int y, final int z, final BaseBlock baseBlock) {
|
||||
final int i = MainUtil.CACHE_I[y][x][z];
|
||||
final int j = MainUtil.CACHE_J[y][x][z];
|
||||
BaseBlock[] array = baseblocks[i];
|
||||
if (array == null) {
|
||||
array = (baseblocks[i] = new BaseBlock[4096]);
|
||||
}
|
||||
array[j] = baseBlock;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* _____ _ _ _____ _
|
||||
* | __ \| | | | / ____| | |
|
||||
* | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| |
|
||||
* | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` |
|
||||
* | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| |
|
||||
* |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_|
|
||||
* | |
|
||||
* |_|
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.util.PatternUtil;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public abstract class BasicQueueCoordinator extends QueueCoordinator {
|
||||
|
||||
private final String world;
|
||||
private final ConcurrentHashMap<Long, LocalChunk> blockChunks = new ConcurrentHashMap<>();
|
||||
private long modified;
|
||||
private LocalChunk lastWrappedChunk;
|
||||
private int lastX = Integer.MIN_VALUE;
|
||||
private int lastZ = Integer.MIN_VALUE;
|
||||
private boolean setbiome = false;
|
||||
|
||||
private GlobalBlockQueue globalBlockQueue;
|
||||
|
||||
public BasicQueueCoordinator(String world) {
|
||||
this.world = world;
|
||||
this.modified = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public LocalChunk getLocalChunk(int x, int z) {
|
||||
return new LocalChunk(this, x, z) {
|
||||
// Allow implementation-specific custom stuff here
|
||||
};
|
||||
}
|
||||
|
||||
@Override public abstract BlockState getBlock(int x, int y, int z);
|
||||
|
||||
@Override public final String getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override public final int size() {
|
||||
return blockChunks.size();
|
||||
}
|
||||
|
||||
@Override public final void setModified(long modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, @Nonnull Pattern pattern) {
|
||||
return setBlock(x, y, z, PatternUtil.apply(pattern, x, y, z));
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BaseBlock id) {
|
||||
if ((y > 255) || (y < 0)) {
|
||||
return false;
|
||||
}
|
||||
LocalChunk chunk = getChunk(x >> 4, z >> 4);
|
||||
chunk.setBlock(x & 15, y, z & 15, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, BlockState id) {
|
||||
// Trying to mix BlockState and BaseBlock leads to all kinds of issues.
|
||||
// Since BaseBlock has more features than BlockState, simply convert
|
||||
// all BlockStates to BaseBlocks
|
||||
return setBlock(x, y, z, id.toBaseBlock());
|
||||
}
|
||||
|
||||
@Override public final boolean setBiome(int x, int z, BiomeType biomeType) {
|
||||
LocalChunk chunk = getChunk(x >> 4, z >> 4);
|
||||
chunk.setBiome(x & 15, z & 15, biomeType);
|
||||
setbiome = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public final boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
LocalChunk chunk = getChunk(x >> 4, z >> 4);
|
||||
chunk.setTile(x, y, z, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public final boolean settingBiome() {
|
||||
return setbiome;
|
||||
}
|
||||
|
||||
public final void setChunk(LocalChunk chunk) {
|
||||
this.blockChunks.put(chunk.longHash(), chunk);
|
||||
}
|
||||
|
||||
private LocalChunk getChunk(final int chunkX, final int ChunkZ) {
|
||||
if (chunkX != lastX || ChunkZ != lastZ) {
|
||||
lastX = chunkX;
|
||||
lastZ = ChunkZ;
|
||||
long pair = (long) (chunkX) << 32 | (ChunkZ) & 0xFFFFFFFFL;
|
||||
lastWrappedChunk = this.blockChunks.get(pair);
|
||||
if (lastWrappedChunk == null) {
|
||||
lastWrappedChunk = this.getLocalChunk(chunkX, ChunkZ);
|
||||
LocalChunk previous = this.blockChunks.put(pair, lastWrappedChunk);
|
||||
if (previous == null) {
|
||||
return lastWrappedChunk;
|
||||
}
|
||||
lastWrappedChunk = previous;
|
||||
}
|
||||
}
|
||||
return lastWrappedChunk;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -31,26 +31,24 @@ import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ChunkBlockQueue extends ScopedLocalBlockQueue {
|
||||
public class ChunkQueueCoordinator extends ScopedQueueCoordinator {
|
||||
|
||||
public final BiomeType[] biomeGrid;
|
||||
public final BlockState[][][] result;
|
||||
private final int width;
|
||||
private final int length;
|
||||
@Deprecated private final int area;
|
||||
private final BlockVector3 bot;
|
||||
private final BlockVector3 top;
|
||||
|
||||
public ChunkBlockQueue(BlockVector3 bot, BlockVector3 top, boolean biomes) {
|
||||
public ChunkQueueCoordinator(BlockVector3 bot, BlockVector3 top, boolean biomes) {
|
||||
super(null, Location.at("", 0, 0, 0), Location.at("", 15, 255, 15));
|
||||
this.width = top.getX() - bot.getX() + 1;
|
||||
this.length = top.getZ() - bot.getZ() + 1;
|
||||
this.area = width * length;
|
||||
this.result = new BlockState[256][][];
|
||||
this.biomeGrid = biomes ? new BiomeType[width * length] : null;
|
||||
this.bot = bot;
|
@ -25,45 +25,32 @@
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class DelegateLocalBlockQueue extends LocalBlockQueue {
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
private final LocalBlockQueue parent;
|
||||
public class DelegateQueueCoordinator extends QueueCoordinator {
|
||||
|
||||
public DelegateLocalBlockQueue(LocalBlockQueue parent) {
|
||||
super(parent == null ? null : parent.getWorld());
|
||||
private final QueueCoordinator parent;
|
||||
|
||||
public DelegateQueueCoordinator(QueueCoordinator parent) {
|
||||
this.parent = parent;
|
||||
|
||||
if (parent != null) {
|
||||
this.setForceSync(parent.isForceSync());
|
||||
this.setChunkObject(parent.getChunkObject());
|
||||
}
|
||||
}
|
||||
|
||||
public LocalBlockQueue getParent() {
|
||||
public QueueCoordinator getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override public boolean next() {
|
||||
return parent.next();
|
||||
}
|
||||
|
||||
@Override public void startSet(boolean parallel) {
|
||||
if (parent != null) {
|
||||
parent.startSet(parallel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void endSet(boolean parallel) {
|
||||
if (parent != null) {
|
||||
parent.endSet(parallel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
if (parent != null) {
|
||||
return parent.size();
|
||||
@ -71,19 +58,6 @@ public class DelegateLocalBlockQueue extends LocalBlockQueue {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void optimize() {
|
||||
if (parent != null) {
|
||||
parent.optimize();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public long getModified() {
|
||||
if (parent != null) {
|
||||
return parent.getModified();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public void setModified(long modified) {
|
||||
if (parent != null) {
|
||||
parent.setModified(modified);
|
||||
@ -110,36 +84,16 @@ public class DelegateLocalBlockQueue extends LocalBlockQueue {
|
||||
return parent.setBiome(x, z, biome);
|
||||
}
|
||||
|
||||
@Override public boolean setBiome() {
|
||||
return parent.setBiome();
|
||||
@Override public boolean settingBiome() {
|
||||
return parent.settingBiome();
|
||||
}
|
||||
|
||||
@Override public String getWorld() {
|
||||
return parent.getWorld();
|
||||
}
|
||||
|
||||
@Override public void flush() {
|
||||
if (parent != null) {
|
||||
parent.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void refreshChunk(int x, int z) {
|
||||
if (parent != null) {
|
||||
parent.refreshChunk(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void fixChunkLighting(int x, int z) {
|
||||
if (parent != null) {
|
||||
parent.fixChunkLighting(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void regenChunk(int x, int z) {
|
||||
if (parent != null) {
|
||||
parent.regenChunk(x, z);
|
||||
}
|
||||
@Override public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return parent.setTile(x, y, z, tag);
|
||||
}
|
||||
|
||||
@Override public boolean enqueue() {
|
@ -25,169 +25,21 @@
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.PlotSquared;
|
||||
import com.plotsquared.core.util.task.RunnableVal2;
|
||||
import com.plotsquared.core.util.task.TaskManager;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class GlobalBlockQueue {
|
||||
|
||||
private final int PARALLEL_THREADS;
|
||||
private final ConcurrentLinkedDeque<LocalBlockQueue> activeQueues;
|
||||
private final ConcurrentLinkedDeque<LocalBlockQueue> inactiveQueues;
|
||||
private final ConcurrentLinkedDeque<Runnable> runnables;
|
||||
private final AtomicBoolean running;
|
||||
private final int targetTime;
|
||||
private QueueProvider provider;
|
||||
/**
|
||||
* Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the
|
||||
* server
|
||||
*/
|
||||
private long last;
|
||||
private long secondLast;
|
||||
private long lastSuccess;
|
||||
private double lastPeriod = 0;
|
||||
private final RunnableVal2<Long, LocalBlockQueue> SET_TASK =
|
||||
new RunnableVal2<Long, LocalBlockQueue>() {
|
||||
@Override public void run(Long free, LocalBlockQueue queue) {
|
||||
do {
|
||||
boolean more = queue.next();
|
||||
if (!more) {
|
||||
lastSuccess = last;
|
||||
if (inactiveQueues.size() == 0 && activeQueues.size() == 0) {
|
||||
runEmptyTasks();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while ((lastPeriod =
|
||||
((GlobalBlockQueue.this.secondLast = System.currentTimeMillis())
|
||||
- GlobalBlockQueue.this.last)) < free);
|
||||
}
|
||||
};
|
||||
private final ConcurrentLinkedDeque<QueueCoordinator> activeQueues;
|
||||
@Getter @Setter private QueueProvider provider;
|
||||
|
||||
public GlobalBlockQueue(QueueProvider provider, int threads, int targetTime) {
|
||||
public GlobalBlockQueue(QueueProvider provider) {
|
||||
this.provider = provider;
|
||||
this.activeQueues = new ConcurrentLinkedDeque<>();
|
||||
this.inactiveQueues = new ConcurrentLinkedDeque<>();
|
||||
this.runnables = new ConcurrentLinkedDeque<>();
|
||||
this.running = new AtomicBoolean();
|
||||
this.targetTime = targetTime;
|
||||
this.PARALLEL_THREADS = threads;
|
||||
}
|
||||
|
||||
public QueueProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(QueueProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public LocalBlockQueue getNewQueue(String world, boolean autoQueue) {
|
||||
LocalBlockQueue queue = provider.getNewQueue(world);
|
||||
// Auto-inject into the queue
|
||||
PlotSquared.platform().getInjector().injectMembers(queue);
|
||||
if (autoQueue) {
|
||||
inactiveQueues.add(queue);
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
public boolean stop() {
|
||||
if (!running.get()) {
|
||||
return false;
|
||||
}
|
||||
running.set(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean runTask() {
|
||||
if (running.get()) {
|
||||
return false;
|
||||
}
|
||||
running.set(true);
|
||||
TaskManager.runTaskRepeat(new Runnable() {
|
||||
@Override public void run() {
|
||||
if (inactiveQueues.isEmpty() && activeQueues.isEmpty()) {
|
||||
lastSuccess = System.currentTimeMillis();
|
||||
lastPeriod = 0;
|
||||
GlobalBlockQueue.this.runEmptyTasks();
|
||||
return;
|
||||
}
|
||||
// Server laggy? Skip.
|
||||
if (lastPeriod > targetTime) {
|
||||
lastPeriod -= targetTime;
|
||||
return;
|
||||
}
|
||||
SET_TASK.value1 = 50 + Math.min(
|
||||
(50 + GlobalBlockQueue.this.last) - (GlobalBlockQueue.this.last =
|
||||
System.currentTimeMillis()),
|
||||
GlobalBlockQueue.this.secondLast - System.currentTimeMillis());
|
||||
SET_TASK.value2 = GlobalBlockQueue.this.getNextQueue();
|
||||
if (SET_TASK.value2 == null) {
|
||||
return;
|
||||
}
|
||||
if (!PlotSquared.get().isMainThread(Thread.currentThread())) {
|
||||
throw new IllegalStateException(
|
||||
"This shouldn't be possible for placement to occur off the main thread");
|
||||
}
|
||||
// Disable the async catcher as it can't discern async vs parallel
|
||||
SET_TASK.value2.startSet(true);
|
||||
try {
|
||||
if (PARALLEL_THREADS <= 1) {
|
||||
SET_TASK.run();
|
||||
} else {
|
||||
ArrayList<Thread> threads = new ArrayList<>();
|
||||
for (int i = 0; i < PARALLEL_THREADS; i++) {
|
||||
threads.add(new Thread(SET_TASK));
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
thread.start();
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
// Enable it again (note that we are still on the main thread)
|
||||
SET_TASK.value2.endSet(true);
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
public QueueStage getStage(LocalBlockQueue queue) {
|
||||
if (activeQueues.contains(queue)) {
|
||||
return QueueStage.ACTIVE;
|
||||
} else if (inactiveQueues.contains(queue)) {
|
||||
return QueueStage.INACTIVE;
|
||||
}
|
||||
return QueueStage.NONE;
|
||||
}
|
||||
|
||||
public boolean isStage(LocalBlockQueue queue, QueueStage stage) {
|
||||
switch (stage) {
|
||||
case ACTIVE:
|
||||
return activeQueues.contains(queue);
|
||||
case INACTIVE:
|
||||
return inactiveQueues.contains(queue);
|
||||
case NONE:
|
||||
return !activeQueues.contains(queue) && !inactiveQueues.contains(queue);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -196,155 +48,23 @@ public class GlobalBlockQueue {
|
||||
* @param queue todo
|
||||
* @return true if added to queue, false otherwise
|
||||
*/
|
||||
public boolean enqueue(LocalBlockQueue queue) {
|
||||
public boolean enqueue(QueueCoordinator queue) {
|
||||
boolean success = false;
|
||||
success = inactiveQueues.remove(queue);
|
||||
if (queue.size() > 0 && !activeQueues.contains(queue)) {
|
||||
queue.optimize();
|
||||
success = activeQueues.add(queue);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public void dequeue(LocalBlockQueue queue) {
|
||||
inactiveQueues.remove(queue);
|
||||
public void dequeue(QueueCoordinator queue) {
|
||||
activeQueues.remove(queue);
|
||||
}
|
||||
|
||||
public List<LocalBlockQueue> getAllQueues() {
|
||||
ArrayList<LocalBlockQueue> list =
|
||||
new ArrayList<>(activeQueues.size() + inactiveQueues.size());
|
||||
list.addAll(inactiveQueues);
|
||||
list.addAll(activeQueues);
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<LocalBlockQueue> getActiveQueues() {
|
||||
public List<QueueCoordinator> getActiveQueues() {
|
||||
return new ArrayList<>(activeQueues);
|
||||
}
|
||||
|
||||
public List<LocalBlockQueue> getInactiveQueues() {
|
||||
return new ArrayList<>(inactiveQueues);
|
||||
}
|
||||
|
||||
public void flush(LocalBlockQueue queue) {
|
||||
SET_TASK.value1 = Long.MAX_VALUE;
|
||||
SET_TASK.value2 = queue;
|
||||
if (SET_TASK.value2 == null) {
|
||||
return;
|
||||
}
|
||||
if (PlotSquared.get().isMainThread(Thread.currentThread())) {
|
||||
throw new IllegalStateException("Must be flushed on the main thread!");
|
||||
}
|
||||
// Disable the async catcher as it can't discern async vs parallel
|
||||
SET_TASK.value2.startSet(true);
|
||||
try {
|
||||
if (PARALLEL_THREADS <= 1) {
|
||||
SET_TASK.run();
|
||||
} else {
|
||||
ArrayList<Thread> threads = new ArrayList<>();
|
||||
for (int i = 0; i < PARALLEL_THREADS; i++) {
|
||||
Thread thread = new Thread(SET_TASK);
|
||||
thread.setName("PlotSquared Flush Task");
|
||||
threads.add(thread);
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
thread.start();
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
// Enable it again (note that we are still on the main thread)
|
||||
SET_TASK.value2.endSet(true);
|
||||
dequeue(queue);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalBlockQueue getNextQueue() {
|
||||
long now = System.currentTimeMillis();
|
||||
while (!activeQueues.isEmpty()) {
|
||||
LocalBlockQueue queue = activeQueues.peek();
|
||||
if (queue != null && queue.size() > 0) {
|
||||
queue.setModified(now);
|
||||
return queue;
|
||||
} else {
|
||||
activeQueues.poll();
|
||||
}
|
||||
}
|
||||
int size = inactiveQueues.size();
|
||||
if (size > 0) {
|
||||
Iterator<LocalBlockQueue> iter = inactiveQueues.iterator();
|
||||
try {
|
||||
int total = 0;
|
||||
LocalBlockQueue firstNonEmpty = null;
|
||||
while (iter.hasNext()) {
|
||||
LocalBlockQueue queue = iter.next();
|
||||
long age = now - queue.getModified();
|
||||
total += queue.size();
|
||||
if (queue.size() == 0) {
|
||||
if (age > 60000) {
|
||||
iter.remove();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (firstNonEmpty == null) {
|
||||
firstNonEmpty = queue;
|
||||
}
|
||||
if (total > 64) {
|
||||
firstNonEmpty.setModified(now);
|
||||
return firstNonEmpty;
|
||||
}
|
||||
if (age > 1000) {
|
||||
queue.setModified(now);
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return activeQueues.size() == 0 && inactiveQueues.size() == 0;
|
||||
}
|
||||
|
||||
public boolean addEmptyTask(final Runnable whenDone) {
|
||||
if (this.isDone()) {
|
||||
// Run
|
||||
this.runEmptyTasks();
|
||||
if (whenDone != null) {
|
||||
whenDone.run();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (whenDone != null) {
|
||||
this.runnables.add(whenDone);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private synchronized void runEmptyTasks() {
|
||||
if (this.runnables.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final ConcurrentLinkedDeque<Runnable> tmp = new ConcurrentLinkedDeque<>(this.runnables);
|
||||
this.runnables.clear();
|
||||
for (final Runnable runnable : tmp) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
public enum QueueStage {
|
||||
INACTIVE, ACTIVE, NONE
|
||||
return activeQueues.size() == 0;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,80 @@
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.util.MainUtil;
|
||||
import com.plotsquared.core.util.MathMan;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class LocalChunk {
|
||||
public final BasicQueueCoordinator parent;
|
||||
public final int z;
|
||||
public final int x;
|
||||
|
||||
public BaseBlock[][] baseblocks;
|
||||
public BiomeType[][] biomes;
|
||||
public HashMap<BlockVector3, CompoundTag> tiles = null;
|
||||
|
||||
public LocalChunk(BasicQueueCoordinator parent, int x, int z) {
|
||||
this.parent = parent;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
baseblocks = new BaseBlock[16][];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent queue this chunk belongs to
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public BasicQueueCoordinator getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setBiome(int x, int z, BiomeType biomeType) {
|
||||
if (this.biomes == null) {
|
||||
this.biomes = new BiomeType[16][];
|
||||
}
|
||||
BiomeType[] index = this.biomes[x];
|
||||
if (index == null) {
|
||||
index = this.biomes[x] = new BiomeType[16];
|
||||
}
|
||||
index[z] = biomeType;
|
||||
}
|
||||
|
||||
public long longHash() {
|
||||
return MathMan.pairInt(x, z);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return MathMan.pair((short) x, (short) z);
|
||||
}
|
||||
|
||||
public void setBlock(final int x, final int y, final int z, final BaseBlock baseBlock) {
|
||||
final int i = MainUtil.CACHE_I[y][x][z];
|
||||
final int j = MainUtil.CACHE_J[y][x][z];
|
||||
BaseBlock[] array = baseblocks[i];
|
||||
if (array == null) {
|
||||
array = (baseblocks[i] = new BaseBlock[4096]);
|
||||
}
|
||||
array[j] = baseBlock;
|
||||
}
|
||||
|
||||
public void setTile(final int x, final int y, final int z, final CompoundTag tag) {
|
||||
if (tiles == null) {
|
||||
tiles = new HashMap<>();
|
||||
}
|
||||
tiles.put(BlockVector3.at(x, y, z), tag);
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
@ -35,16 +36,17 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class LocationOffsetDelegateLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
public class LocationOffsetDelegateQueueCoordinator extends DelegateQueueCoordinator {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger("P2/" + LocationOffsetDelegateLocalBlockQueue.class.getSimpleName());
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger("P2/" + LocationOffsetDelegateQueueCoordinator.class.getSimpleName());
|
||||
|
||||
private final boolean[][] canPlace;
|
||||
private final int blockX;
|
||||
private final int blockZ;
|
||||
|
||||
public LocationOffsetDelegateLocalBlockQueue(final boolean[][] canPlace, final int blockX,
|
||||
final int blockZ, @Nullable LocalBlockQueue parent) {
|
||||
public LocationOffsetDelegateQueueCoordinator(final boolean[][] canPlace, final int blockX,
|
||||
final int blockZ, @Nullable QueueCoordinator parent) {
|
||||
super(parent);
|
||||
this.canPlace = canPlace;
|
||||
this.blockX = blockX;
|
||||
@ -78,4 +80,7 @@ public class LocationOffsetDelegateLocalBlockQueue extends DelegateLocalBlockQue
|
||||
return super.setBiome(x, y, biome);
|
||||
}
|
||||
|
||||
@Override public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return super.setTile(x, y, z, tag);
|
||||
}
|
||||
}
|
@ -25,15 +25,17 @@
|
||||
*/
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
|
||||
public class OffsetLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
public class OffsetQueueCoordinator extends DelegateQueueCoordinator {
|
||||
private final int ox;
|
||||
private final int oy;
|
||||
private final int oz;
|
||||
|
||||
public OffsetLocalBlockQueue(LocalBlockQueue parent, int ox, int oy, int oz) {
|
||||
public OffsetQueueCoordinator(QueueCoordinator parent, int ox, int oy, int oz) {
|
||||
super(parent);
|
||||
this.ox = ox;
|
||||
this.oy = oy;
|
||||
@ -47,4 +49,12 @@ public class OffsetLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
@Override public boolean setBlock(int x, int y, int z, BaseBlock id) {
|
||||
return super.setBlock(ox + x, oy + y, oz + z, id);
|
||||
}
|
||||
|
||||
@Override public boolean setBlock(int x, int y, int z, Pattern pattern) {
|
||||
return super.setBlock(ox + x, oy + y, oz + z, pattern);
|
||||
}
|
||||
|
||||
@Override public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return super.setTile(ox + x, oy + y, oz + z, tag);
|
||||
}
|
||||
}
|
@ -28,59 +28,31 @@ 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.player.PlotPlayer;
|
||||
import com.plotsquared.core.util.PatternUtil;
|
||||
import com.plotsquared.core.util.SchematicHandler;
|
||||
import com.plotsquared.core.util.StringMan;
|
||||
import com.plotsquared.core.util.WorldUtil;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.math.BlockVector2;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class LocalBlockQueue {
|
||||
public abstract class QueueCoordinator {
|
||||
|
||||
@Getter @Setter private boolean forceSync = false;
|
||||
@Getter @Setter @Nullable private Object chunkObject;
|
||||
|
||||
@Inject private SchematicHandler schematicHandler;
|
||||
@Inject private WorldUtil worldUtil;
|
||||
@Inject private GlobalBlockQueue blockQueue;
|
||||
|
||||
/**
|
||||
* Needed for compatibility with FAWE.
|
||||
*
|
||||
* @param world unused
|
||||
*/
|
||||
@Deprecated public LocalBlockQueue(String world) {
|
||||
public QueueCoordinator() {
|
||||
PlotSquared.platform().getInjector().injectMembers(this);
|
||||
}
|
||||
|
||||
public ScopedLocalBlockQueue getForChunk(int x, int z) {
|
||||
int bx = x << 4;
|
||||
int bz = z << 4;
|
||||
return new ScopedLocalBlockQueue(this, Location.at(getWorld(), bx, 0, bz),
|
||||
Location.at(getWorld(), bx + 15, 255, bz + 255));
|
||||
}
|
||||
|
||||
public abstract boolean next();
|
||||
|
||||
public abstract void startSet(boolean parallel);
|
||||
|
||||
public abstract void endSet(boolean parallel);
|
||||
|
||||
public abstract int size();
|
||||
|
||||
public abstract void optimize();
|
||||
|
||||
public abstract long getModified();
|
||||
|
||||
public abstract void setModified(long modified);
|
||||
|
||||
/**
|
||||
@ -99,48 +71,22 @@ public abstract class LocalBlockQueue {
|
||||
return setBlock(x, y, z, PatternUtil.apply(pattern, x, y, z));
|
||||
}
|
||||
|
||||
public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
this.schematicHandler.restoreTile(this, tag, x, y, z);
|
||||
return true;
|
||||
}
|
||||
public abstract boolean setTile(int x, int y, int z, CompoundTag tag);
|
||||
|
||||
public abstract BlockState getBlock(int x, int y, int z);
|
||||
|
||||
public abstract boolean setBiome(int x, int z, BiomeType biome);
|
||||
|
||||
public abstract boolean setBiome();
|
||||
public abstract boolean settingBiome();
|
||||
|
||||
public abstract String getWorld();
|
||||
|
||||
public abstract void flush();
|
||||
|
||||
public final void setModified() {
|
||||
setModified(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public abstract void refreshChunk(int x, int z);
|
||||
|
||||
public abstract void fixChunkLighting(int x, int z);
|
||||
|
||||
public abstract void regenChunk(int x, int z);
|
||||
|
||||
public final void regenChunkSafe(int x, int z) {
|
||||
regenChunk(x, z);
|
||||
fixChunkLighting(x, z);
|
||||
BlockVector2 loc = BlockVector2.at(x, z);
|
||||
|
||||
for (final PlotPlayer<?> pp : PlotSquared.platform().getPlayerManager().getPlayers()) {
|
||||
Location pLoc = pp.getLocation();
|
||||
if (!StringMan.isEqual(getWorld(), pLoc.getWorldName()) || !pLoc.getChunkLocation()
|
||||
.equals(loc)) {
|
||||
continue;
|
||||
}
|
||||
pp.teleport(pLoc.withY(this.worldUtil.getHighestBlockSynchronous(getWorld(), pLoc.getX(), pLoc.getZ())));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean enqueue() {
|
||||
return blockQueue.enqueue(this);
|
||||
return this.blockQueue.enqueue(this);
|
||||
}
|
||||
|
||||
public void setCuboid(Location pos1, Location pos2, BlockState block) {
|
@ -26,23 +26,23 @@
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
public abstract class QueueProvider {
|
||||
public static QueueProvider of(final Class<? extends LocalBlockQueue> primary,
|
||||
final Class<? extends LocalBlockQueue> fallback) {
|
||||
public static QueueProvider of(final Class<? extends QueueCoordinator> primary,
|
||||
final Class<? extends QueueCoordinator> fallback) {
|
||||
return new QueueProvider() {
|
||||
|
||||
private boolean failed = false;
|
||||
|
||||
@Override public LocalBlockQueue getNewQueue(String world) {
|
||||
@Override public QueueCoordinator getNewQueue(String world) {
|
||||
if (!failed) {
|
||||
try {
|
||||
return (LocalBlockQueue) primary.getConstructors()[0].newInstance(world);
|
||||
return (QueueCoordinator) primary.getConstructors()[0].newInstance(world);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return (LocalBlockQueue) fallback.getConstructors()[0].newInstance(world);
|
||||
return (QueueCoordinator) fallback.getConstructors()[0].newInstance(world);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -51,5 +51,5 @@ public abstract class QueueProvider {
|
||||
};
|
||||
}
|
||||
|
||||
public abstract LocalBlockQueue getNewQueue(String world);
|
||||
public abstract QueueCoordinator getNewQueue(String world);
|
||||
}
|
||||
|
@ -26,12 +26,13 @@
|
||||
package com.plotsquared.core.queue;
|
||||
|
||||
import com.plotsquared.core.location.Location;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
|
||||
public class ScopedLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
public class ScopedQueueCoordinator extends DelegateQueueCoordinator {
|
||||
private final int minX;
|
||||
private final int minY;
|
||||
private final int minZ;
|
||||
@ -44,7 +45,7 @@ public class ScopedLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
private final int dy;
|
||||
private final int dz;
|
||||
|
||||
public ScopedLocalBlockQueue(LocalBlockQueue parent, Location min, Location max) {
|
||||
public ScopedQueueCoordinator(QueueCoordinator parent, Location min, Location max) {
|
||||
super(parent);
|
||||
this.minX = min.getX();
|
||||
this.minY = min.getY();
|
||||
@ -86,6 +87,11 @@ public class ScopedLocalBlockQueue extends DelegateLocalBlockQueue {
|
||||
.setBlock(x + minX, y + minY, z + minZ, pattern);
|
||||
}
|
||||
|
||||
@Override public boolean setTile(int x, int y, int z, CompoundTag tag) {
|
||||
return x >= 0 && x <= dx && y >= 0 && y <= dy && z >= 0 && z <= dz && super
|
||||
.setTile(x + minX, y + minY, z + minZ, tag);
|
||||
}
|
||||
|
||||
public Location getMin() {
|
||||
return Location.at(this.getWorld(), this.minX, this.minY, this.minZ);
|
||||
}
|
Loading…
Reference in New Issue
Block a user