diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 9e154d14df..26c357fc20 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.extent.TracingExtent; import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer; +import com.sk89q.worldedit.extent.buffer.internal.BatchingExtent; import com.sk89q.worldedit.extent.cache.LastAccessExtentCache; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBagExtent; @@ -200,6 +201,7 @@ public String getDisplayName() { private @Nullable SideEffectExtent sideEffectExtent; private final SurvivalModeExtent survivalExtent; + private @Nullable BatchingExtent batchingExtent; private @Nullable ChunkBatchingExtent chunkBatchingExtent; private final BlockBagExtent blockBagExtent; @SuppressWarnings("deprecation") @@ -269,6 +271,7 @@ public String getDisplayName() { this.bypassReorderHistory = traceIfNeeded(new DataValidatorExtent(extent, world)); // This extent can be skipped by calling rawSetBlock() + extent = traceIfNeeded(batchingExtent = new BatchingExtent(extent)); @SuppressWarnings("deprecation") MultiStageReorder reorder = new MultiStageReorder(extent, false); extent = traceIfNeeded(reorderExtent = reorder); @@ -618,10 +621,12 @@ public void setBatchingChunks(boolean batchingChunks) { } return; } + assert batchingExtent != null : "same nullness as chunkBatchingExtent"; if (!batchingChunks && isBatchingChunks()) { internalFlushSession(); } chunkBatchingExtent.setEnabled(batchingChunks); + batchingExtent.setEnabled(!batchingChunks); } /** @@ -650,6 +655,8 @@ public void disableBuffering() { setReorderMode(ReorderMode.NONE); if (chunkBatchingExtent != null) { chunkBatchingExtent.setEnabled(false); + assert batchingExtent != null : "same nullness as chunkBatchingExtent"; + batchingExtent.setEnabled(true); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/internal/BatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/internal/BatchingExtent.java new file mode 100644 index 0000000000..674e0a5db7 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/internal/BatchingExtent.java @@ -0,0 +1,111 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * 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 . + */ + +package com.sk89q.worldedit.extent.buffer.internal; + +import com.google.common.base.Throwables; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.AbstractBufferingExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.RunContext; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.collection.BlockMap; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; + +/** + * An extent that buffers all changes until completed. + */ +public class BatchingExtent extends AbstractBufferingExtent { + + private final BlockMap blockMap = BlockMap.createForBaseBlock(); + private boolean enabled; + + public BatchingExtent(Extent extent) { + this(extent, true); + } + + public BatchingExtent(Extent extent, boolean enabled) { + super(extent); + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean commitRequired() { + return enabled; + } + + @Override + public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { + if (!enabled) { + return setDelegateBlock(location, block); + } + blockMap.put(location, block.toBaseBlock()); + return true; + } + + @Override + protected BaseBlock getBufferedFullBlock(BlockVector3 position) { + if (!enabled) { + // Early exit if we're not enabled. + return null; + } + return blockMap.get(position); + } + + @Override + protected Operation commitBefore() { + if (!commitRequired()) { + return null; + } + return new Operation() { + + @Override + public Operation resume(RunContext run) throws WorldEditException { + try { + blockMap.forEach((position, block) -> { + try { + getExtent().setBlock(position, block); + } catch (WorldEditException e) { + throw new RuntimeException(e); + } + }); + } catch (RuntimeException e) { + Throwables.throwIfInstanceOf(e.getCause(), WorldEditException.class); + throw e; + } + blockMap.clear(); + return null; + } + + @Override + public void cancel() { + } + }; + } + +} 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 49834fe9a4..51ac0ccf7a 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 @@ -31,7 +31,6 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -91,18 +90,11 @@ protected Operation commitBefore() { } return new Operation() { - // we get modified between create/resume -- only create this on resume to prevent CME - private Iterator iterator; - @Override public Operation resume(RunContext run) throws WorldEditException { - if (iterator == null) { - List blockVectors = new ArrayList<>(blockMap.keySet()); - RegionOptimizedVectorSorter.sort(blockVectors); - iterator = blockVectors.iterator(); - } - while (iterator.hasNext()) { - BlockVector3 position = iterator.next(); + List blockVectors = new ArrayList<>(blockMap.keySet()); + RegionOptimizedVectorSorter.sort(blockVectors); + for (BlockVector3 position : blockVectors) { BaseBlock block = blockMap.get(position); getExtent().setBlock(position, block); }