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 902cd7af7d..895a6cf213 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; @@ -202,6 +203,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") @@ -256,10 +258,11 @@ public String getDisplayName() { // These extents are ALWAYS used sideEffectExtent = new SideEffectExtent(world); - NativeWorld nativeInterface; - if (WorldEdit.getInstance().getConfiguration().chunkSectionEditing + NativeWorld nativeInterface = null; + boolean usingNativeInterface = WorldEdit.getInstance().getConfiguration().chunkSectionEditing && world instanceof AbstractWorld internalWorld - && (nativeInterface = internalWorld.getNativeInterface()) != null) { + && (nativeInterface = internalWorld.getNativeInterface()) != null; + if (usingNativeInterface) { extent = traceIfNeeded(new SectionBufferingExtent(nativeInterface, sideEffectExtent)); } else { extent = traceIfNeeded(sideEffectExtent); @@ -278,6 +281,11 @@ public String getDisplayName() { this.bypassReorderHistory = traceIfNeeded(new DataValidatorExtent(extent, world)); // This extent can be skipped by calling rawSetBlock() + if (!usingNativeInterface) { + // We need to ensure that blocks are not immediately committed to the world for masks + // This is done by the SectionBufferingExtent normally, but if we can't use it we need a fallback + extent = traceIfNeeded(batchingExtent = new BatchingExtent(extent)); + } @SuppressWarnings("deprecation") MultiStageReorder reorder = new MultiStageReorder(extent, false); extent = traceIfNeeded(reorderExtent = reorder); @@ -631,6 +639,9 @@ public void setBatchingChunks(boolean batchingChunks) { internalFlushSession(); } chunkBatchingExtent.setEnabled(batchingChunks); + if (batchingExtent != null) { + batchingExtent.setEnabled(!batchingChunks); + } } /** @@ -659,6 +670,9 @@ public void disableBuffering() { setReorderMode(ReorderMode.NONE); if (chunkBatchingExtent != null) { chunkBatchingExtent.setEnabled(false); + if (batchingExtent != null) { + 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() { + } + }; + } + +}