Plex-FAWE/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/ParallelQueueExtent.java

246 lines
9.7 KiB
Java

package com.boydti.fawe.beta.implementation.queue;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.IQueueWrapper;
import com.boydti.fawe.beta.implementation.filter.CountFilter;
import com.boydti.fawe.beta.implementation.filter.DistrFilter;
import com.boydti.fawe.beta.implementation.filter.LinkedFilter;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.processors.BatchProcessorHolder;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
import com.boydti.fawe.object.extent.NullExtent;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.extent.PassthroughExtent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.BlockMaskBuilder;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.IntStream;
public class ParallelQueueExtent extends PassthroughExtent implements IQueueWrapper {
private final World world;
private final QueueHandler handler;
private final BatchProcessorHolder processor;
private final BatchProcessorHolder postProcessor;
private int changes;
private final boolean fastmode;
public ParallelQueueExtent(QueueHandler handler, World world, boolean fastmode) {
super(handler.getQueue(world, new BatchProcessorHolder(), new BatchProcessorHolder()));
this.world = world;
this.handler = handler;
this.processor = (BatchProcessorHolder) getExtent().getProcessor();
this.postProcessor = (BatchProcessorHolder) getExtent().getPostProcessor();
this.fastmode = fastmode;
}
@Override
public IQueueExtent<IQueueChunk> getExtent() {
return (IQueueExtent<IQueueChunk>) super.getExtent();
}
@Override
public boolean cancel() {
if (super.cancel()) {
processor.setProcessor(new NullExtent(this, FaweCache.MANUAL));
postProcessor.setPostProcessor(new NullExtent(this, FaweCache.MANUAL));
return true;
}
return false;
}
private IQueueExtent<IQueueChunk> getNewQueue() {
return wrapQueue(handler.getQueue(this.world, this.processor, this.postProcessor));
}
@Override
public IQueueExtent<IQueueChunk> wrapQueue(IQueueExtent<IQueueChunk> queue) {
// TODO wrap
queue.setProcessor(this.processor);
queue.setPostProcessor(this.postProcessor);
return queue;
}
@Override
public <T extends Filter> T apply(Region region, T filter, boolean full) {
// The chunks positions to iterate over
final Set<BlockVector2> chunks = region.getChunks();
final Iterator<BlockVector2> chunksIter = chunks.iterator();
// Get a pool, to operate on the chunks in parallel
final int size = Math.min(chunks.size(), Settings.IMP.QUEUE.PARALLEL_THREADS);
if (size <= 1 && chunksIter.hasNext()) {
BlockVector2 pos = chunksIter.next();
getExtent().apply(null, filter, region, pos.getX(), pos.getZ(), full);
} else {
final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> {
try {
final Filter newFilter = filter.fork();
// Create a chunk that we will reuse/reset for each operation
final IQueueExtent<IQueueChunk> queue = getNewQueue();
queue.setFastMode(fastmode);
synchronized (queue) {
ChunkFilterBlock block = null;
while (true) {
// Get the next chunk posWeakChunk
final int chunkX;
final int chunkZ;
synchronized (chunksIter) {
if (!chunksIter.hasNext()) {
break;
}
final BlockVector2 pos = chunksIter.next();
chunkX = pos.getX();
chunkZ = pos.getZ();
}
block = queue.apply(block, newFilter, region, chunkX, chunkZ, full);
}
queue.flush();
}
} catch (Throwable e) {
e.printStackTrace();
}
})).toArray(ForkJoinTask[]::new);
// Join filters
for (ForkJoinTask task : tasks) {
if (task != null) {
task.quietlyJoin();
}
}
filter.join();
}
return filter;
}
@Override
public int countBlocks(Region region, Mask searchMask) {
return
// Apply a filter over a region
apply(region, searchMask
.toFilter(new CountFilter()), false) // Adapt the mask to a filter which counts
.getParent() // Get the counter of this mask
.getTotal(); // Get the total from the counter
}
@Override
public <B extends BlockStateHolder<B>> int setBlocks(Region region, B block) throws MaxChangedBlocksException {
return this.changes = apply(region, new BlockMaskBuilder().add(block).build(this).toFilter(new CountFilter())).getParent().getTotal();
}
@Override
public int setBlocks(Region region, Pattern pattern) throws MaxChangedBlocksException {
return this.changes = apply(region, new LinkedFilter<>(pattern, new CountFilter()), true).getChild().getTotal();
}
@Override
public int setBlocks(Set<BlockVector3> vset, Pattern pattern) {
if (vset instanceof Region) {
this.changes = setBlocks((Region) vset, pattern);
}
// TODO optimize parallel
for (BlockVector3 blockVector3 : vset) {
if (pattern.apply(this, blockVector3, blockVector3)) {
this.changes++;
}
}
return this.changes;
}
@Override
public int replaceBlocks(Region region, Mask mask, Pattern pattern)
throws MaxChangedBlocksException {
boolean full = mask.replacesAir();
return this.changes = apply(region, mask.toFilter(pattern), full).getBlocksApplied();
}
@Override
public List<Countable<BlockState>> getBlockDistributionWithData(Region region) {
return apply(region, new DistrFilter(), false).getDistribution();
}
@Override
public List<Countable<BlockType>> getBlockDistribution(Region region) {
return apply(region, new DistrFilter(), false).getTypeDistribution();
}
/**
* Lazily copy a region
*
* @param region
* @return
*/
@Override
public Clipboard lazyCopy(Region region) {
Clipboard clipboard = new WorldCopyClipboard(() -> this, region);
clipboard.setOrigin(region.getMinimumPoint());
return clipboard;
}
/**
* Count the number of blocks of a list of types in a region.
*
* @param region the region
* @param searchBlocks the list of blocks to search
* @return the number of blocks that matched the block
*/
@Override
public int countBlocks(Region region, Set<BaseBlock> searchBlocks) {
Mask mask = new BlockMask(this, searchBlocks);
return countBlocks(region, mask);
}
/**
* Replaces all the blocks matching a given filter, within a given region, to a block
* returned by a given pattern.
*
* @param region the region to replace the blocks within
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
* @param replacement the replacement block
* @return number of blocks affected
* @throws MaxChangedBlocksException thrown if too many blocks are changed
*/
@Override
public <B extends BlockStateHolder<B>> int replaceBlocks(Region region, Set<BaseBlock> filter, B replacement) throws MaxChangedBlocksException {
return replaceBlocks(region, filter, new BlockPattern(replacement));
}
/**
* Replaces all the blocks matching a given filter, within a given region, to a block
* returned by a given pattern.
*
* @param region the region to replace the blocks within
* @param filter a list of block types to match, or null to use {@link com.sk89q.worldedit.function.mask.ExistingBlockMask}
* @param pattern the pattern that provides the new blocks
* @return number of blocks affected
* @throws MaxChangedBlocksException thrown if too many blocks are changed
*/
@Override
public int replaceBlocks(Region region, Set<BaseBlock> filter, Pattern pattern) throws MaxChangedBlocksException {
Mask mask = filter == null ? new ExistingBlockMask(this) : new BlockMask(this, filter);
return replaceBlocks(region, mask, pattern);
}
}