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
16 changed files with 436 additions and 824 deletions

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