Many fixes for buffered extents

This commit is contained in:
Kenzie Togami 2019-07-04 11:43:36 -07:00
parent d27daefd3e
commit 99ee32fe8e
No known key found for this signature in database
GPG Key ID: 5D200B325E157A81
7 changed files with 136 additions and 73 deletions

View File

@ -565,12 +565,12 @@ public class EditSession implements Extent, AutoCloseable {
@Override @Override
public BlockState getBlock(BlockVector3 position) { public BlockState getBlock(BlockVector3 position) {
return world.getBlock(position); return bypassNone.getBlock(position);
} }
@Override @Override
public BaseBlock getFullBlock(BlockVector3 position) { public BaseBlock getFullBlock(BlockVector3 position) {
return world.getFullBlock(position); return bypassNone.getFullBlock(position);
} }
/** /**

View File

@ -0,0 +1,48 @@
package com.sk89q.worldedit.extent;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Optional;
/**
* Base extent class for buffering changes between {@link #setBlock(BlockVector3, BlockStateHolder)}
* and the delegate extent. This class ensures that {@link #getBlock(BlockVector3)} is properly
* handled, by returning buffered blocks.
*/
public abstract class AbstractBufferingExtent extends AbstractDelegateExtent {
/**
* Create a new instance.
*
* @param extent the extent
*/
protected AbstractBufferingExtent(Extent extent) {
super(extent);
}
@Override
public abstract <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block) throws WorldEditException;
protected final <T extends BlockStateHolder<T>> boolean setDelegateBlock(BlockVector3 location, T block) throws WorldEditException {
return super.setBlock(location, block);
}
@Override
public BlockState getBlock(BlockVector3 position) {
return getBufferedBlock(position)
.map(BaseBlock::toImmutableState)
.orElseGet(() -> super.getBlock(position));
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
return getBufferedBlock(position)
.orElseGet(() -> super.getFullBlock(position));
}
protected abstract Optional<BaseBlock> getBufferedBlock(BlockVector3 position);
}

View File

@ -21,16 +21,16 @@ package com.sk89q.worldedit.extent.buffer;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock; 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.BlockStateHolder;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -38,7 +38,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* Buffers changes to an {@link Extent} and allows retrieval of the changed blocks, * Buffers changes to an {@link Extent} and allows retrieval of the changed blocks,
* without modifying the underlying extent. * without modifying the underlying extent.
*/ */
public class ExtentBuffer extends AbstractDelegateExtent { public class ExtentBuffer extends AbstractBufferingExtent {
private final Map<BlockVector3, BaseBlock> buffer = Maps.newHashMap(); private final Map<BlockVector3, BaseBlock> buffer = Maps.newHashMap();
private final Mask mask; private final Mask mask;
@ -67,23 +67,11 @@ public class ExtentBuffer extends AbstractDelegateExtent {
} }
@Override @Override
public BlockState getBlock(BlockVector3 position) { protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (mask.test(position)) { if (mask.test(position)) {
return getOrDefault(position).toImmutableState(); return Optional.of(buffer.computeIfAbsent(position, (pos -> getExtent().getFullBlock(pos))));
} }
return super.getBlock(position); return Optional.empty();
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
if (mask.test(position)) {
return getOrDefault(position);
}
return super.getFullBlock(position);
}
private BaseBlock getOrDefault(BlockVector3 position) {
return buffer.computeIfAbsent(position, (pos -> getExtent().getFullBlock(pos)));
} }
@Override @Override

View File

@ -22,14 +22,13 @@ package com.sk89q.worldedit.extent.reorder;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable; import com.google.common.collect.TreeBasedTable;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext; import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock; 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.BlockStateHolder;
import java.util.Comparator; import java.util.Comparator;
@ -37,6 +36,7 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@ -45,7 +45,7 @@ import java.util.Set;
* loaded repeatedly, however it does take more memory due to caching the * loaded repeatedly, however it does take more memory due to caching the
* blocks. * blocks.
*/ */
public class ChunkBatchingExtent extends AbstractDelegateExtent { public class ChunkBatchingExtent extends AbstractBufferingExtent {
/** /**
* Comparator optimized for sorting chunks by the region file they reside * Comparator optimized for sorting chunks by the region file they reside
@ -92,7 +92,7 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent {
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException { public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (!enabled) { if (!enabled) {
return getExtent().setBlock(location, block); return setDelegateBlock(location, block);
} }
BlockVector2 chunkPos = getChunkPos(location); BlockVector2 chunkPos = getChunkPos(location);
BlockVector3 inChunkPos = getInChunkPos(location); BlockVector3 inChunkPos = getInChunkPos(location);
@ -102,28 +102,11 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent {
} }
@Override @Override
public BlockState getBlock(BlockVector3 position) { protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
BaseBlock internal = getInternalBlock(position);
if (internal != null) {
return internal.toImmutableState();
}
return super.getBlock(position);
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
BaseBlock internal = getInternalBlock(position);
if (internal != null) {
return internal;
}
return super.getFullBlock(position);
}
private BaseBlock getInternalBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) { if (!containedBlocks.contains(position)) {
return null; return Optional.empty();
} }
return batches.get(getChunkPos(position), getInChunkPos(position)); return Optional.of(batches.get(getChunkPos(position), getInChunkPos(position)));
} }
@Override @Override
@ -134,19 +117,20 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent {
return new Operation() { return new Operation() {
// we get modified between create/resume -- only create this on resume to prevent CME // we get modified between create/resume -- only create this on resume to prevent CME
private Iterator<Map<BlockVector3, BaseBlock>> batchIterator; private Iterator<Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>>> batchIterator;
@Override @Override
public Operation resume(RunContext run) throws WorldEditException { public Operation resume(RunContext run) throws WorldEditException {
if (batchIterator == null) { if (batchIterator == null) {
batchIterator = batches.rowMap().values().iterator(); batchIterator = batches.rowMap().entrySet().iterator();
} }
if (!batchIterator.hasNext()) { if (!batchIterator.hasNext()) {
return null; return null;
} }
Map<BlockVector3, BaseBlock> next = batchIterator.next(); Map.Entry<BlockVector2, Map<BlockVector3, BaseBlock>> next = batchIterator.next();
for (Map.Entry<BlockVector3, BaseBlock> block : next.entrySet()) { BlockVector3 chunkOffset = next.getKey().toBlockVector3().shl(4);
getExtent().setBlock(block.getKey(), block.getValue()); for (Map.Entry<BlockVector3, BaseBlock> block : next.getValue().entrySet()) {
getExtent().setBlock(block.getKey().add(chunkOffset), block.getValue());
containedBlocks.remove(block.getKey()); containedBlocks.remove(block.getKey());
} }
batchIterator.remove(); batchIterator.remove();

View File

@ -20,7 +20,7 @@
package com.sk89q.worldedit.extent.reorder; package com.sk89q.worldedit.extent.reorder;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.AbstractBufferingExtent;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue; import com.sk89q.worldedit.function.operation.OperationQueue;
@ -36,13 +36,17 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/** /**
* Re-orders blocks into several stages. * Re-orders blocks into several stages.
*/ */
public class MultiStageReorder extends AbstractDelegateExtent implements ReorderingExtent { public class MultiStageReorder extends AbstractBufferingExtent implements ReorderingExtent {
private static final Map<BlockType, PlacementPriority> priorityMap = new HashMap<>(); private static final Map<BlockType, PlacementPriority> priorityMap = new HashMap<>();
@ -139,6 +143,7 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL); priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL);
} }
private final Set<BlockVector3> containedBlocks = new HashSet<>();
private Map<PlacementPriority, LocatedBlockList> stages = new HashMap<>(); private Map<PlacementPriority, LocatedBlockList> stages = new HashMap<>();
private boolean enabled; private boolean enabled;
@ -212,7 +217,7 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
@Override @Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException { public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 location, B block) throws WorldEditException {
if (!enabled) { if (!enabled) {
return super.setBlock(location, block); return setDelegateBlock(location, block);
} }
BlockState existing = getBlock(location); BlockState existing = getBlock(location);
@ -240,9 +245,21 @@ public class MultiStageReorder extends AbstractDelegateExtent implements Reorder
} }
stages.get(priority).add(location, block); stages.get(priority).add(location, block);
containedBlocks.add(location);
return !existing.equalsFuzzy(block); return !existing.equalsFuzzy(block);
} }
@Override
protected Optional<BaseBlock> getBufferedBlock(BlockVector3 position) {
if (!containedBlocks.contains(position)) {
return Optional.empty();
}
return stages.values().stream()
.map(blocks -> blocks.get(position))
.filter(Objects::nonNull)
.findAny();
}
@Override @Override
public Operation commitBefore() { public Operation commitBefore() {
if (!commitRequired()) { if (!commitRequired()) {

View File

@ -366,6 +366,28 @@ public final class BlockVector3 {
return shr(n, n, n); return shr(n, n, n);
} }
/**
* Shift all components left.
*
* @param x the value to shift x by
* @param y the value to shift y by
* @param z the value to shift z by
* @return a new vector
*/
public BlockVector3 shl(int x, int y, int z) {
return at(this.x << x, this.y << y, this.z << z);
}
/**
* Shift all components left by {@code n}.
*
* @param n the value to shift by
* @return a new vector
*/
public BlockVector3 shl(int n) {
return shl(n, n, n);
}
/** /**
* Get the length of the vector. * Get the length of the vector.
* *

View File

@ -21,68 +21,72 @@ package com.sk89q.worldedit.util.collection;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.LocatedBlock; import com.sk89q.worldedit.util.LocatedBlock;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map;
/** /**
* Wrapper around a list of blocks located in the world. * Wrapper around a list of blocks located in the world.
*/ */
public class LocatedBlockList implements Iterable<LocatedBlock> { public class LocatedBlockList implements Iterable<LocatedBlock> {
private final List<LocatedBlock> list; private final Map<BlockVector3, LocatedBlock> map = new LinkedHashMap<>();
public LocatedBlockList() { public LocatedBlockList() {
list = new ArrayList<>();
} }
public LocatedBlockList(Collection<? extends LocatedBlock> collection) { public LocatedBlockList(Collection<? extends LocatedBlock> collection) {
list = new ArrayList<>(collection); for (LocatedBlock locatedBlock : collection) {
map.put(locatedBlock.getLocation(), locatedBlock);
}
} }
public void add(LocatedBlock setBlockCall) { public void add(LocatedBlock setBlockCall) {
checkNotNull(setBlockCall); checkNotNull(setBlockCall);
list.add(setBlockCall); map.put(setBlockCall.getLocation(), setBlockCall);
} }
public <B extends BlockStateHolder<B>> void add(BlockVector3 location, B block) { public <B extends BlockStateHolder<B>> void add(BlockVector3 location, B block) {
add(new LocatedBlock(location, block.toBaseBlock())); add(new LocatedBlock(location, block.toBaseBlock()));
} }
public boolean containsLocation(BlockVector3 location) {
return map.containsKey(location);
}
public @Nullable BaseBlock get(BlockVector3 location) {
return map.get(location).getBlock();
}
public int size() { public int size() {
return list.size(); return map.size();
} }
public void clear() { public void clear() {
list.clear(); map.clear();
} }
@Override @Override
public Iterator<LocatedBlock> iterator() { public Iterator<LocatedBlock> iterator() {
return list.iterator(); return map.values().iterator();
} }
public Iterator<LocatedBlock> reverseIterator() { public Iterator<LocatedBlock> reverseIterator() {
return new Iterator<LocatedBlock>() { List<LocatedBlock> data = new ArrayList<>(map.values());
Collections.reverse(data);
private final ListIterator<LocatedBlock> backingIterator = list.listIterator(list.size()); return data.iterator();
@Override
public boolean hasNext() {
return backingIterator.hasPrevious();
}
@Override
public LocatedBlock next() {
return backingIterator.previous();
}
};
} }
} }