From e25a6d21cd49ef9afabadc786ce794d40120b767 Mon Sep 17 00:00:00 2001 From: wizjany Date: Sat, 3 Aug 2019 12:59:24 -0400 Subject: [PATCH] Add -e/-b/-m flags to //stack and //move, to match copy and paste. Fixes WORLDEDIT-3935. --- .../java/com/sk89q/worldedit/EditSession.java | 65 +++++++++++++++++-- .../worldedit/command/RegionCommands.java | 45 +++++++++++-- .../extent/buffer/ForgetfulExtentBuffer.java | 57 ++++++++++++++-- .../function/biome/BiomeReplace.java | 19 ++++-- .../function/pattern/BiomePattern.java | 37 +++++++++++ .../worldedit/regions/AbstractFlatRegion.java | 40 ++++++++++++ .../worldedit/world/biome/BiomeType.java | 9 ++- 7 files changed, 253 insertions(+), 19 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/BiomePattern.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java 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 183f7542f..aff049e9a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -40,6 +40,7 @@ import com.sk89q.worldedit.extent.world.FastModeExtent; import com.sk89q.worldedit.extent.world.SurvivalModeExtent; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; +import com.sk89q.worldedit.function.biome.BiomeReplace; import com.sk89q.worldedit.function.block.BlockDistributionCounter; import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.block.Counter; @@ -66,6 +67,7 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.WaterloggedRemover; import com.sk89q.worldedit.function.util.RegionOffset; import com.sk89q.worldedit.function.visitor.DownwardVisitor; +import com.sk89q.worldedit.function.visitor.FlatRegionVisitor; import com.sk89q.worldedit.function.visitor.LayerVisitor; import com.sk89q.worldedit.function.visitor.NonRisingVisitor; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; @@ -1206,7 +1208,8 @@ public class EditSession implements Extent, AutoCloseable { } /** - * Stack a cuboid region. + * Stack a cuboid region. For compatibility, entities are copied by biomes are not. + * Use {@link #stackCuboidRegion(Region, BlockVector3, int, boolean, boolean, Mask)} to fine tune. * * @param region the region to stack * @param dir the direction to stack @@ -1216,6 +1219,23 @@ public class EditSession implements Extent, AutoCloseable { * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean copyAir) throws MaxChangedBlocksException { + return stackCuboidRegion(region, dir, count, true, false, copyAir ? null : new ExistingBlockMask(this)); + } + + /** + * Stack a cuboid region. + * + * @param region the region to stack + * @param dir the direction to stack + * @param count the number of times to stack + * @param copyEntities true to copy entities + * @param copyBiomes true to copy biomes + * @param mask source mask for the operation (only matching blocks are copied) + * @return number of blocks affected + * @throws MaxChangedBlocksException thrown if too many blocks are changed + */ + public int stackCuboidRegion(Region region, BlockVector3 dir, int count, + boolean copyEntities, boolean copyBiomes, Mask mask) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(dir); checkArgument(count >= 1, "count >= 1 required"); @@ -1225,8 +1245,10 @@ public class EditSession implements Extent, AutoCloseable { ForwardExtentCopy copy = new ForwardExtentCopy(this, region, this, to); copy.setRepetitions(count); copy.setTransform(new AffineTransform().translate(dir.multiply(size))); - if (!copyAir) { - copy.setSourceMask(new ExistingBlockMask(this)); + copy.setCopyingEntities(copyEntities); + copy.setCopyingBiomes(copyBiomes); + if (mask != null) { + copy.setSourceMask(mask); } Operations.completeLegacy(copy); return copy.getAffected(); @@ -1244,9 +1266,29 @@ public class EditSession implements Extent, AutoCloseable { * @throws MaxChangedBlocksException thrown if too many blocks are changed */ public int moveRegion(Region region, BlockVector3 dir, int distance, boolean copyAir, Pattern replacement) throws MaxChangedBlocksException { + return moveRegion(region, dir, distance, true, false, copyAir ? new ExistingBlockMask(this) : null, replacement); + } + + /** + * Move the blocks in a region a certain direction. + * + * @param region the region to move + * @param dir the direction + * @param distance the distance to move + * @param moveEntities true to move entities + * @param copyBiomes true to copy biomes (source biome is unchanged) + * @param mask source mask for the operation (only matching blocks are moved) + * @param replacement the replacement pattern to fill in after moving, or null to use air + * @return number of blocks moved + * @throws MaxChangedBlocksException thrown if too many blocks are changed + * @throws IllegalArgumentException thrown if the region is not a flat region, but copyBiomes is true + */ + public int moveRegion(Region region, BlockVector3 dir, int distance, + boolean moveEntities, boolean copyBiomes, Mask mask, Pattern replacement) throws MaxChangedBlocksException { checkNotNull(region); checkNotNull(dir); checkArgument(distance >= 1, "distance >= 1 required"); + checkArgument(!copyBiomes || region instanceof FlatRegion, "can't copy biomes from non-flat region"); BlockVector3 to = region.getMinimumPoint(); @@ -1261,9 +1303,13 @@ public class EditSession implements Extent, AutoCloseable { ForwardExtentCopy copy = new ForwardExtentCopy(this, region, buffer, to); copy.setTransform(new AffineTransform().translate(dir.multiply(distance))); copy.setSourceFunction(remove); // Remove - copy.setRemovingEntities(true); - if (!copyAir) { - copy.setSourceMask(new ExistingBlockMask(this)); + + copy.setCopyingEntities(moveEntities); + copy.setRemovingEntities(moveEntities); + copy.setCopyingBiomes(copyBiomes); + + if (mask != null) { + copy.setSourceMask(mask); } // Then we need to copy the buffer to the world @@ -1271,6 +1317,13 @@ public class EditSession implements Extent, AutoCloseable { RegionVisitor visitor = new RegionVisitor(buffer.asRegion(), replace); OperationQueue operation = new OperationQueue(copy, visitor); + + if (copyBiomes) { + BiomeReplace biomeReplace = new BiomeReplace(this, buffer); + FlatRegionVisitor biomeVisitor = new FlatRegionVisitor((FlatRegion) buffer.asRegion(), biomeReplace); + operation.offer(biomeVisitor); + } + Operations.completeLegacy(operation); return copy.getAffected(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 0871f8b8b..e4241dc49 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.generator.FloraGenerator; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.mask.NoiseFilter2D; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; @@ -56,6 +57,7 @@ import com.sk89q.worldedit.util.TreeGenerator.TreeType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.annotation.param.ArgFlag; import org.enginehub.piston.annotation.param.Switch; import java.util.ArrayList; @@ -293,10 +295,27 @@ public class RegionCommands { @Switch(name = 's', desc = "Shift the selection to the target location") boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") - boolean ignoreAirBlocks) throws WorldEditException { + boolean ignoreAirBlocks, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air", def = "") + Mask mask) throws WorldEditException { checkCommandArgument(count >= 1, "Count must be >= 1"); - int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); + Mask combinedMask; + if (ignoreAirBlocks) { + if (mask == null) { + combinedMask = new ExistingBlockMask(editSession); + } else { + combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession)); + } + } else { + combinedMask = mask; + } + + int affected = editSession.moveRegion(region, direction, count, copyEntities, copyBiomes, combinedMask, replace); if (moveSelection) { try { @@ -329,8 +348,26 @@ public class RegionCommands { @Switch(name = 's', desc = "Shift the selection to the last stacked copy") boolean moveSelection, @Switch(name = 'a', desc = "Ignore air blocks") - boolean ignoreAirBlocks) throws WorldEditException { - int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); + boolean ignoreAirBlocks, + @Switch(name = 'e', desc = "Also copy entities") + boolean copyEntities, + @Switch(name = 'b', desc = "Also copy biomes") + boolean copyBiomes, + @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air", def = "") + Mask mask) throws WorldEditException { + + Mask combinedMask; + if (ignoreAirBlocks) { + if (mask == null) { + combinedMask = new ExistingBlockMask(editSession); + } else { + combinedMask = new MaskIntersection(mask, new ExistingBlockMask(editSession)); + } + } else { + combinedMask = mask; + } + + int affected = editSession.stackCuboidRegion(region, direction, count, copyEntities, copyBiomes, combinedMask); if (moveSelection) { try { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java index 0aa410916..299ea5a40 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java @@ -25,12 +25,17 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Mask2D; import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.pattern.BiomePattern; import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.AbstractRegion; +import com.sk89q.worldedit.regions.AbstractFlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; @@ -46,12 +51,16 @@ import java.util.Map; *

This buffer will not attempt to return results from the buffer when * accessor methods (such as {@link #getBlock(BlockVector3)}) are called.

*/ -public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern { +public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern, BiomePattern { private final Map buffer = new LinkedHashMap<>(); + private final Map biomeBuffer = new LinkedHashMap<>(); private final Mask mask; + private final Mask2D biomeMask; private BlockVector3 min = null; + private BlockVector2 min2d = null; private BlockVector3 max = null; + private BlockVector2 max2d = null; /** * Create a new extent buffer that will buffer every change. @@ -71,9 +80,10 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat */ public ForgetfulExtentBuffer(Extent delegate, Mask mask) { super(delegate); - checkNotNull(delegate); checkNotNull(mask); this.mask = mask; + Mask2D bmask = mask.toMask2D(); + this.biomeMask = bmask == null ? Masks.alwaysTrue2D() : bmask; } @Override @@ -100,6 +110,30 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat } } + @Override + public boolean setBiome(BlockVector2 position, BiomeType biome) { + // Update minimum + if (min2d == null) { + min2d = position; + } else { + min2d = min2d.getMinimum(position); + } + + // Update maximum + if (max2d == null) { + max2d = position; + } else { + max2d = max2d.getMaximum(position); + } + + if (biomeMask.test(position)) { + biomeBuffer.put(position, biome); + return true; + } else { + return getExtent().setBiome(position, biome); + } + } + @Override public BaseBlock apply(BlockVector3 pos) { BaseBlock block = buffer.get(pos); @@ -110,13 +144,23 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat } } + @Override + public BiomeType apply(BlockVector2 pos) { + BiomeType biome = biomeBuffer.get(pos); + if (biome != null) { + return biome; + } else { + return BiomeTypes.OCEAN; + } + } + /** * Return a region representation of this buffer. * * @return a region */ public Region asRegion() { - return new AbstractRegion(null) { + return new AbstractFlatRegion(null) { @Override public BlockVector3 getMinimumPoint() { return min != null ? min : BlockVector3.ZERO; @@ -146,6 +190,11 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat public Iterator iterator() { return buffer.keySet().iterator(); } + + @Override + public Iterable asFlatRegion() { + return biomeBuffer.keySet(); + } }; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java index efa53f45a..9b98bd48d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/biome/BiomeReplace.java @@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.FlatRegionFunction; +import com.sk89q.worldedit.function.pattern.BiomePattern; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.world.biome.BiomeType; @@ -33,7 +34,7 @@ import com.sk89q.worldedit.world.biome.BiomeType; public class BiomeReplace implements FlatRegionFunction { private final Extent extent; - private BiomeType biome; + private BiomePattern biome; /** * Create a new instance. @@ -42,15 +43,25 @@ public class BiomeReplace implements FlatRegionFunction { * @param biome a biome */ public BiomeReplace(Extent extent, BiomeType biome) { + this(extent, (BiomePattern) biome); + } + + /** + * Create a new instance. + * + * @param extent the extent to apply this function to + * @param pattern the biome pattern to set + */ + public BiomeReplace(Extent extent, BiomePattern pattern) { checkNotNull(extent); - checkNotNull(biome); + checkNotNull(pattern); this.extent = extent; - this.biome = biome; + this.biome = pattern; } @Override public boolean apply(BlockVector2 position) throws WorldEditException { - return extent.setBiome(position, biome); + return extent.setBiome(position, biome.apply(position)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/BiomePattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/BiomePattern.java new file mode 100644 index 000000000..82147f392 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/BiomePattern.java @@ -0,0 +1,37 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.pattern; + +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.world.biome.BiomeType; + +/** + * Returns a {@link BiomeType} for a given position. + */ +public interface BiomePattern { + + /** + * Return a {@link BiomeType} for the given position. + * + * @param position the position + * @return a block + */ + BiomeType apply(BlockVector2 position); +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java new file mode 100644 index 000000000..a2641d242 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/AbstractFlatRegion.java @@ -0,0 +1,40 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.regions; + +import com.sk89q.worldedit.world.World; + +public abstract class AbstractFlatRegion extends AbstractRegion implements FlatRegion { + + protected AbstractFlatRegion(World world) { + super(world); + } + + @Override + public int getMinimumY() { + return getMinimumPoint().getBlockY(); + } + + @Override + public int getMaximumY() { + return getMaximumPoint().getBlockY(); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java index fe9203c70..d2bfab07e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeType.java @@ -19,13 +19,15 @@ package com.sk89q.worldedit.world.biome; +import com.sk89q.worldedit.function.pattern.BiomePattern; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; /** * All the types of biomes in the game. */ -public class BiomeType implements Keyed { +public class BiomeType implements Keyed, BiomePattern { public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("biome type"); @@ -59,4 +61,9 @@ public class BiomeType implements Keyed { public boolean equals(Object obj) { return obj instanceof BiomeType && this.id.equals(((BiomeType) obj).id); } + + @Override + public BiomeType apply(BlockVector2 position) { + return this; + } }