Skip to content
Permalink
Browse files

Completely rework Arrangers, to match original idea

  • Loading branch information...
kenzierocks committed Jul 23, 2019
1 parent f397233 commit 3bf45d6b6d0154bac018fec068674d53fa493946
Showing with 403 additions and 1,226 deletions.
  1. +2 −2 worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java
  2. +2 −8 ...n/java/com/sk89q/worldedit/{reorder/buffer/ReadOnlyWorldActionBuffer.java → WorldActionUtil.java}
  3. +3 −1 worldedit-core/src/main/java/com/sk89q/worldedit/action/BlockPlacement.java
  4. +3 −1 worldedit-core/src/main/java/com/sk89q/worldedit/action/PerformSideEffects.java
  5. +9 −14 .../java/com/sk89q/worldedit/{reorder/arrange/AttributeKey.java → action/SideEffectWorldAction.java}
  6. +14 −32 worldedit-core/src/main/java/com/sk89q/worldedit/extent/ArrangerExtent.java
  7. +30 −52 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/ChunkBatchingArranger.java
  8. +22 −27 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/ChunkLoadingArranger.java
  9. +16 −51 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/DelayedLightingArranger.java
  10. +7 −19 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/FastModeArranger.java
  11. +13 −35 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/FastReorderArranger.java
  12. +62 −59 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/MultiStageReorderArranger.java
  13. +47 −0 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/WorldActionUtil.java
  14. +2 −12 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/Arranger.java
  15. +41 −4 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/ArrangerContext.java
  16. +0 −89 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/ArrangerContextImpl.java
  17. +9 −20 ...n/java/com/sk89q/worldedit/reorder/arrange/{WorldActionOutputStream.java → ArrangerPipeline.java}
  18. +110 −0 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/ArrangerPipelineImpl.java
  19. +2 −8 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/DelegatingArranger.java
  20. +2 −8 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/ForwardingArranger.java
  21. +3 −14 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/OptionalArranger.java
  22. +0 −53 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/SimpleAttributeKey.java
  23. +3 −8 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/arrange/WorldArranger.java
  24. +0 −63 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/BufferConditions.java
  25. +0 −182 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/MutableArrayWorldActionBuffer.java
  26. +0 −103 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/MutableWorldActionBuffer.java
  27. +0 −92 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/ReadOnlyArrayWorldActionBuffer.java
  28. +0 −84 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/SharedArrayWorldActionBuffer.java
  29. +0 −184 worldedit-core/src/main/java/com/sk89q/worldedit/reorder/buffer/WorldActionBuffer.java
  30. +1 −1 worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/PositionList.java
@@ -102,10 +102,10 @@
import com.sk89q.worldedit.reorder.FastReorderArranger;
import com.sk89q.worldedit.reorder.MultiStageReorderArranger;
import com.sk89q.worldedit.reorder.arrange.Arranger;
import com.sk89q.worldedit.reorder.arrange.ArrangerPipeline;
import com.sk89q.worldedit.reorder.arrange.DelegatingArranger;
import com.sk89q.worldedit.reorder.arrange.ForwardingArranger;
import com.sk89q.worldedit.reorder.arrange.OptionalArranger;
import com.sk89q.worldedit.reorder.arrange.WorldActionOutputStream;
import com.sk89q.worldedit.reorder.arrange.WorldArranger;
import com.sk89q.worldedit.util.Countable;
import com.sk89q.worldedit.util.Direction;
@@ -249,7 +249,7 @@ public String getDisplayName() {
new ChunkLoadingArranger(),
new WorldArranger(world)
);
extent = new ArrangerExtent(world, WorldActionOutputStream.create(arrangers));
extent = new ArrangerExtent(world, ArrangerPipeline.create(arrangers));
extent = survivalExtent = new SurvivalModeExtent(extent, world);
extent = quirkExtent = new BlockQuirkExtent(extent, world);
extent = cacheExtent = new LastAccessExtentCache(extent);
@@ -17,13 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.sk89q.worldedit.reorder.buffer;

public interface ReadOnlyWorldActionBuffer extends WorldActionBuffer {

@Override
default boolean isReadOnly() {
return true;
}
package com.sk89q.worldedit;

public class WorldActionUtil {
}
@@ -34,7 +34,7 @@
/**
* Represents the placement of a block.
*/
public final class BlockPlacement implements BlockWorldAction {
public final class BlockPlacement implements SideEffectWorldAction {

public static BlockPlacement create(BlockVector3 location, BaseBlock oldBlock, BaseBlock block) {
return create(location, oldBlock, block, SideEffect.getDefault());
@@ -69,10 +69,12 @@ public BaseBlock getBlock() {
return block;
}

@Override
public ImmutableSet<SideEffect> getSideEffects() {
return sideEffects;
}

@Override
public BlockPlacement withSideEffects(Collection<SideEffect> sideEffects) {
return new BlockPlacement(position, oldBlock, block, sideEffects);
}
@@ -33,7 +33,7 @@
/**
* Performs side-effects at a block location.
*/
public final class PerformSideEffects implements BlockWorldAction {
public final class PerformSideEffects implements SideEffectWorldAction {

public static PerformSideEffects create(BlockVector3 location) {
return create(location, SideEffect.getDefault());
@@ -56,10 +56,12 @@ public BlockVector3 getPosition() {
return position;
}

@Override
public ImmutableSet<SideEffect> getSideEffects() {
return sideEffects;
}

@Override
public PerformSideEffects withSideEffects(Collection<SideEffect> sideEffects) {
return new PerformSideEffects(position, sideEffects);
}
@@ -17,21 +17,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.sk89q.worldedit.reorder.arrange;
package com.sk89q.worldedit.action;

/**
* Each unique instance of this interface corresponds to a unique entry in the attribute map of a
* pipeline.
*/
public interface AttributeKey<T> {
import com.google.common.collect.ImmutableSet;

import java.util.Collection;

public interface SideEffectWorldAction extends BlockWorldAction {

ImmutableSet<SideEffect> getSideEffects();

static <T> AttributeKey<T> create(String toString) {
return new AttributeKey<T>() {
@Override
public String toString() {
return toString;
}
};
}
SideEffectWorldAction withSideEffects(Collection<SideEffect> sideEffects);

}
@@ -22,70 +22,52 @@
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.action.BlockPlacement;
import com.sk89q.worldedit.action.SideEffect;
import com.sk89q.worldedit.action.WorldAction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.reorder.arrange.WorldActionOutputStream;
import com.sk89q.worldedit.reorder.buffer.MutableArrayWorldActionBuffer;
import com.sk89q.worldedit.reorder.buffer.MutableWorldActionBuffer;
import com.sk89q.worldedit.reorder.arrange.ArrangerPipeline;
import com.sk89q.worldedit.util.collection.BlockMap;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;

import java.util.ArrayList;
import java.util.List;

/**
* Extent that hands off block placements to a {@link WorldActionOutputStream}.
* Extent that hands off block placements to a {@link ArrangerPipeline}.
*/
public class ArrangerExtent extends AbstractDelegateExtent {

private static final int BUFFER_AMOUNT = 1 << 16;

private final BlockMap<BaseBlock> blockMap = BlockMap.createForBaseBlock();
private final WorldActionOutputStream outputStream;
private MutableWorldActionBuffer buffer;
private final ArrangerPipeline pipeline;
private List<WorldAction> actions = new ArrayList<>();

public ArrangerExtent(Extent extent, WorldActionOutputStream outputStream) {
public ArrangerExtent(Extent extent, ArrangerPipeline pipeline) {
super(extent);
this.outputStream = outputStream;
}

private MutableWorldActionBuffer buffer() {
if (buffer == null) {
buffer = MutableArrayWorldActionBuffer.allocate(BUFFER_AMOUNT);
}
return buffer;
this.pipeline = pipeline;
}

@Override
public <T extends BlockStateHolder<T>> boolean setBlock(BlockVector3 location, T block) throws WorldEditException {
MutableWorldActionBuffer b = buffer();
b.put(BlockPlacement.create(location, getExtent().getFullBlock(location),
actions.add(BlockPlacement.create(location, getExtent().getFullBlock(location),
block.toBaseBlock(), SideEffect.getDefault()));

// current buffer is full, pipe it and start a new buffer
if (!b.hasRemaining()) {
flushBuffer();
}

return true;
}

private void flushBuffer() {
buffer.flip();
outputStream.write(buffer);
buffer = null;
}

@Override
protected Operation commitBefore() {
return new Operation() {
@Override
public Operation resume(RunContext run) {
if (buffer != null) {
flushBuffer();
}
outputStream.flush();
// ensure that GC can free the list when the pipeline stops
// referencing it
List<WorldAction> actionCopy = actions;
actions = new ArrayList<>();
pipeline.rearrange(actionCopy);
return null;
}

@@ -19,71 +19,49 @@

package com.sk89q.worldedit.reorder;

import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.action.BlockWorldAction;
import com.sk89q.worldedit.action.WorldAction;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.RegionOptimizedComparator;
import com.sk89q.worldedit.reorder.arrange.Arranger;
import com.sk89q.worldedit.reorder.arrange.ArrangerContext;
import com.sk89q.worldedit.reorder.arrange.WorldActionOutputStream;
import com.sk89q.worldedit.reorder.arrange.SimpleAttributeKey;
import com.sk89q.worldedit.reorder.buffer.MutableArrayWorldActionBuffer;
import com.sk89q.worldedit.reorder.buffer.MutableWorldActionBuffer;
import com.sk89q.worldedit.reorder.buffer.WorldActionBuffer;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

public final class ChunkBatchingArranger implements Arranger {

private static final SimpleAttributeKey<Map<BlockVector3, List<BlockWorldAction>>>
CHUNK_STORAGE = SimpleAttributeKey.create("chunkStorage", Object2ObjectOpenHashMap::new);
private static final SimpleAttributeKey<Boolean> IMMEDIATE_PENDING
= SimpleAttributeKey.create("immediatePending", () -> false);
private static final Comparator<WorldAction> WORLD_ACTION_COMPARATOR =
Comparator.comparing(WorldActionUtil::worldActionAsBlockVector3,
RegionOptimizedComparator.INSTANCE);

@Override
public void onWrite(ArrangerContext stream, WorldActionBuffer buffer) {
// for actions we don't batch:
List<WorldAction> immediateTransfer = new ArrayList<>();
Map<BlockVector3, List<BlockWorldAction>> chunkStorage = CHUNK_STORAGE.get(stream);
while (buffer.hasRemaining()) {
WorldAction next = buffer.get();
if (!(next instanceof BlockWorldAction)) {
immediateTransfer.add(next);
public void rearrange(ArrangerContext context) {
if (context.getActionCount() == 0) {
context.markGroup(0, 0);
return;
}
List<WorldAction> actions = context.getActionWriteList();
actions.sort(WORLD_ACTION_COMPARATOR);
BlockVector2 chunkPos = null;
int groupStart = 0;
for (int i = 0; i < actions.size(); i++) {
BlockVector3 blockPos = WorldActionUtil.worldActionAsBlockVector3(actions.get(i));
if (blockPos == null) {
blockPos = WorldActionUtil.MIN_VECTOR;
}
BlockVector2 thisCp = blockPos.toBlockVector2().shr(4);
if (chunkPos == null) {
chunkPos = thisCp;
continue;
}
BlockWorldAction bwa = (BlockWorldAction) next;
BlockVector3 chunkKey = bwa.getPosition().shr(4);
List<BlockWorldAction> chunkMap = chunkStorage.computeIfAbsent(chunkKey,
k -> new ObjectArrayList<>());
chunkMap.add(bwa);
}
if (immediateTransfer.size() > 0) {
stream.write(MutableArrayWorldActionBuffer.wrap(
immediateTransfer.toArray(new WorldAction[0])
));
IMMEDIATE_PENDING.set(stream, true);
if (!chunkPos.equals(thisCp)) {
chunkPos = thisCp;
context.markGroup(groupStart, i);
groupStart = i;
}
}
context.markGroup(groupStart, actions.size());
}

private void write(WorldActionOutputStream stream, List<BlockWorldAction> chunkMap) {
MutableWorldActionBuffer data = MutableArrayWorldActionBuffer.allocate(chunkMap.size());
chunkMap.toArray(data.array());
stream.write(data);
// flush after each chunk, to mark it as a group
stream.flush();
}

@Override
public void onFlush(ArrangerContext stream) {
if (IMMEDIATE_PENDING.get(stream)) {
// flush immediate first
stream.flush();
IMMEDIATE_PENDING.set(stream, false);
}
// write & flush chunks
CHUNK_STORAGE.get(stream).forEach((chunkKey, chunkMap) -> write(stream, chunkMap));
}
}
@@ -19,43 +19,38 @@

package com.sk89q.worldedit.reorder;

import com.sk89q.worldedit.action.BlockWorldAction;
import com.sk89q.worldedit.action.ChunkLoad;
import com.sk89q.worldedit.action.ChunkWorldAction;
import com.sk89q.worldedit.action.WorldAction;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.reorder.arrange.Arranger;
import com.sk89q.worldedit.reorder.arrange.ArrangerContext;
import com.sk89q.worldedit.reorder.buffer.MutableArrayWorldActionBuffer;
import com.sk89q.worldedit.reorder.buffer.WorldActionBuffer;

import java.util.HashSet;
import java.util.Set;

public class ChunkLoadingArranger implements Arranger {

@Override
public void onWrite(ArrangerContext context, WorldActionBuffer buffer) {
Set<BlockVector2> chunkLoads = new HashSet<>();
while (buffer.hasRemaining()) {
WorldAction next = buffer.get();
if (next instanceof ChunkWorldAction && !(next instanceof ChunkLoad)) {
chunkLoads.add(((ChunkWorldAction) next).getPosition());
} else if (next instanceof BlockWorldAction) {
chunkLoads.add(((BlockWorldAction) next).getPosition().toBlockVector2().shr(4));
public void rearrange(ArrangerContext context) {
BlockVector2 chunkPos = null;
int start = 0;
for (int i = 0; i < context.getActionCount(); i++) {
BlockVector3 blockPos = WorldActionUtil.worldActionAsBlockVector3(context.getAction(i));
if (blockPos == null) {
continue;
}
BlockVector2 thisCp = blockPos.toBlockVector2().shr(4);
if (chunkPos == null) {
chunkPos = thisCp;
continue;
}
if (!chunkPos.equals(thisCp)) {
context.getActionWriteList().add(start, ChunkLoad.create(chunkPos));
chunkPos = thisCp;
i++; // account for add
start = i;
}
}
buffer.flip();
if (chunkLoads.size() > 0) {
context.write(MutableArrayWorldActionBuffer.wrap(
chunkLoads.stream().map(ChunkLoad::create).toArray(WorldAction[]::new)
));
if (chunkPos != null) {
context.getActionWriteList().add(start, ChunkLoad.create(chunkPos));
}
context.write(buffer);
}

@Override
public void onFlush(ArrangerContext context) {
context.flush();
context.markGroup(0, context.getActionCount());
}
}

0 comments on commit 3bf45d6

Please sign in to comment.
You can’t perform that action at this time.