begin new block setting/chunk pipeline

This will ultimately replace both the GlobalBlockQueue and the ChunkTask stuff
This commit is contained in:
dordsor21 2020-07-17 13:22:33 +01:00
parent 8eb903ad72
commit def9a1bcf8
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
16 changed files with 436 additions and 824 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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 {
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);
}

View File

@ -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);
}