From d27daefd3e896aba95beb8454c73659998a7fd19 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Sun, 23 Jun 2019 01:03:18 -0700 Subject: [PATCH] Implement getBlock for chunk batching extent Also improve speed of comparators, by using ::comparingX and bitwise ops. --- .../extent/reorder/ChunkBatchingExtent.java | 66 +++++++++++++++---- .../sk89q/worldedit/math/BlockVector2.java | 33 +++++++--- .../sk89q/worldedit/math/BlockVector3.java | 40 +++++++---- 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java index b8f6082c4..30940e942 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java @@ -19,22 +19,25 @@ package com.sk89q.worldedit.extent.reorder; +import com.google.common.collect.Table; +import com.google.common.collect.TreeBasedTable; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.RunContext; -import com.sk89q.worldedit.function.operation.SetLocatedBlocks; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.util.collection.LocatedBlockList; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.Map; +import java.util.Set; /** * A special extent that batches changes into Minecraft chunks. This helps @@ -49,10 +52,12 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent { * in. This allows for file caches to be used while loading the chunk. */ private static final Comparator REGION_OPTIMIZED_SORT = - Comparator.comparing((BlockVector2 vec) -> vec.divide(32), BlockVector2.COMPARING_GRID_ARRANGEMENT) + Comparator.comparing((BlockVector2 vec) -> vec.shr(5), BlockVector2.COMPARING_GRID_ARRANGEMENT) .thenComparing(BlockVector2.COMPARING_GRID_ARRANGEMENT); - private final SortedMap batches = new TreeMap<>(REGION_OPTIMIZED_SORT); + private final Table batches = + TreeBasedTable.create(REGION_OPTIMIZED_SORT, BlockVector3.sortByCoordsYzx()); + private final Set containedBlocks = new HashSet<>(); private boolean enabled; public ChunkBatchingExtent(Extent extent) { @@ -76,16 +81,51 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent { return enabled; } + private BlockVector2 getChunkPos(BlockVector3 location) { + return location.shr(4).toBlockVector2(); + } + + private BlockVector3 getInChunkPos(BlockVector3 location) { + return BlockVector3.at(location.getX() & 15, location.getY(), location.getZ() & 15); + } + @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { if (!enabled) { return getExtent().setBlock(location, block); } - BlockVector2 chunkPos = BlockVector2.at(location.getBlockX() >> 4, location.getBlockZ() >> 4); - batches.computeIfAbsent(chunkPos, k -> new LocatedBlockList()).add(location, block); + BlockVector2 chunkPos = getChunkPos(location); + BlockVector3 inChunkPos = getInChunkPos(location); + batches.put(chunkPos, inChunkPos, block.toBaseBlock()); + containedBlocks.add(location); return true; } + @Override + public BlockState getBlock(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)) { + return null; + } + return batches.get(getChunkPos(position), getInChunkPos(position)); + } + @Override protected Operation commitBefore() { if (!commitRequired()) { @@ -94,17 +134,21 @@ public class ChunkBatchingExtent extends AbstractDelegateExtent { return new Operation() { // we get modified between create/resume -- only create this on resume to prevent CME - private Iterator batchIterator; + private Iterator> batchIterator; @Override public Operation resume(RunContext run) throws WorldEditException { if (batchIterator == null) { - batchIterator = batches.values().iterator(); + batchIterator = batches.rowMap().values().iterator(); } if (!batchIterator.hasNext()) { return null; } - new SetLocatedBlocks(getExtent(), batchIterator.next()).resume(run); + Map next = batchIterator.next(); + for (Map.Entry block : next.entrySet()) { + getExtent().setBlock(block.getKey(), block.getValue()); + containedBlocks.remove(block.getKey()); + } batchIterator.remove(); return this; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java index 92548fc99..6e2dd17e1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector2.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.math; -import com.google.common.collect.ComparisonChain; import com.sk89q.worldedit.math.transform.AffineTransform; import java.util.Comparator; @@ -28,7 +27,7 @@ import java.util.Comparator; * An immutable 2-dimensional vector. */ public final class BlockVector2 { - + public static final BlockVector2 ZERO = new BlockVector2(0, 0); public static final BlockVector2 UNIT_X = new BlockVector2(1, 0); public static final BlockVector2 UNIT_Z = new BlockVector2(0, 1); @@ -48,12 +47,8 @@ public final class BlockVector2 { * cdef * */ - public static final Comparator COMPARING_GRID_ARRANGEMENT = (a, b) -> { - return ComparisonChain.start() - .compare(a.getBlockZ(), b.getBlockZ()) - .compare(a.getBlockX(), b.getBlockX()) - .result(); - }; + public static final Comparator COMPARING_GRID_ARRANGEMENT = + Comparator.comparingInt(BlockVector2::getZ).thenComparingInt(BlockVector2::getX); public static BlockVector2 at(double x, double z) { return at((int) Math.floor(x), (int) Math.floor(z)); @@ -303,6 +298,27 @@ public final class BlockVector2 { return divide(n, n); } + /** + * Shift all components right. + * + * @param x the value to shift x by + * @param z the value to shift z by + * @return a new vector + */ + public BlockVector2 shr(int x, int z) { + return at(this.x >> x, this.z >> z); + } + + /** + * Shift all components right by {@code n}. + * + * @param n the value to shift by + * @return a new vector + */ + public BlockVector2 shr(int n) { + return shr(n, n); + } + /** * Get the length of the vector. * @@ -532,5 +548,4 @@ public final class BlockVector2 { public String toString() { return "(" + x + ", " + z + ")"; } - } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java index ffb6f6347..71d058bd9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/BlockVector3.java @@ -19,13 +19,12 @@ package com.sk89q.worldedit.math; -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.collect.ComparisonChain; import com.sk89q.worldedit.math.transform.AffineTransform; import java.util.Comparator; +import static com.google.common.base.Preconditions.checkArgument; + /** * An immutable 3-dimensional vector. */ @@ -64,18 +63,15 @@ public final class BlockVector3 { // thread-safe initialization idiom private static final class YzxOrderComparator { - private static final Comparator YZX_ORDER = (a, b) -> { - return ComparisonChain.start() - .compare(a.y, b.y) - .compare(a.z, b.z) - .compare(a.x, b.x) - .result(); - }; + private static final Comparator YZX_ORDER = + Comparator.comparingInt(BlockVector3::getY) + .thenComparingInt(BlockVector3::getZ) + .thenComparingInt(BlockVector3::getX); } /** * Returns a comparator that sorts vectors first by Y, then Z, then X. - * + * *

* Useful for sorting by chunk block storage order. */ @@ -348,6 +344,28 @@ public final class BlockVector3 { return divide(n, n, n); } + /** + * Shift all components right. + * + * @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 shr(int x, int y, int z) { + return at(this.x >> x, this.y >> y, this.z >> z); + } + + /** + * Shift all components right by {@code n}. + * + * @param n the value to shift by + * @return a new vector + */ + public BlockVector3 shr(int n) { + return shr(n, n, n); + } + /** * Get the length of the vector. *