Skip to content
Permalink
Browse files

Add -e/-b/-m flags to //stack and //move, to match copy and paste.

Fixes WORLDEDIT-3935.
  • Loading branch information...
wizjany committed Aug 3, 2019
1 parent df9d766 commit 46aac02bc593e2fe4b8ba4cefe169cb6b3a7fd32
@@ -40,6 +40,7 @@
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.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;
@@ -1204,7 +1206,8 @@ public int naturalizeCuboidBlocks(Region region) throws MaxChangedBlocksExceptio
}

/**
* 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
@@ -1214,6 +1217,23 @@ public int naturalizeCuboidBlocks(Region region) throws MaxChangedBlocksExceptio
* @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");
@@ -1223,8 +1243,10 @@ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean
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();
@@ -1242,9 +1264,29 @@ public int stackCuboidRegion(Region region, BlockVector3 dir, int count, boolean
* @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();

@@ -1259,16 +1301,27 @@ public int moveRegion(Region region, BlockVector3 dir, int distance, boolean cop
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
BlockReplace replace = new BlockReplace(this, buffer);
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();
@@ -34,6 +34,7 @@
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 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 int move(Player player, EditSession editSession, LocalSession session,
@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 int stack(Player player, EditSession editSession, LocalSession session,
@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 {
@@ -25,12 +25,17 @@
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 @@
* <p>This buffer will not attempt to return results from the buffer when
* accessor methods (such as {@link #getBlock(BlockVector3)}) are called.</p>
*/
public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern {
public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern, BiomePattern {

private final Map<BlockVector3, BaseBlock> buffer = new LinkedHashMap<>();
private final Map<BlockVector2, BiomeType> 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 ForgetfulExtentBuffer(Extent delegate) {
*/
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 ForgetfulExtentBuffer(Extent delegate, Mask mask) {
}
}

@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 BaseBlock apply(BlockVector3 pos) {
}
}

@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 boolean contains(BlockVector3 position) {
public Iterator<BlockVector3> iterator() {
return buffer.keySet().iterator();
}

@Override
public Iterable<BlockVector2> asFlatRegion() {
return biomeBuffer.keySet();
}
};
}
}
@@ -24,6 +24,7 @@
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 @@
public class BiomeReplace implements FlatRegionFunction {

private final Extent extent;
private BiomeType biome;
private BiomePattern biome;

/**
* Create a new instance.
@@ -42,15 +43,25 @@
* @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));
}

}
@@ -0,0 +1,37 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/

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);
}

0 comments on commit 46aac02

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