diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 6509db543..640e07338 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -132,7 +132,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { } // Block & Item for (Material material : Material.values()) { - if (material.isBlock()) { + if (material.isBlock() && !material.isLegacy()) { BlockType.REGISTRY.register(material.getKey().toString(), new BlockType(material.getKey().toString(), blockState -> { // TODO Use something way less hacky than this. ParserContext context = new ParserContext(); @@ -154,7 +154,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { } })); } - if (material.isItem()) { + if (material.isItem() && !material.isLegacy()) { ItemType.REGISTRY.register(material.getKey().toString(), new ItemType(material.getKey().toString())); } } 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 c36f2625f..38766ba6b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -592,10 +592,25 @@ public class EditSession implements Extent, AutoCloseable { * @return height of highest block found or 'minY' */ public int getHighestTerrainBlock(int x, int z, int minY, int maxY) { + return getHighestTerrainBlock(x, z, minY, maxY, null); + } + + /** + * Returns the highest solid 'terrain' block. + * + * @param x the X coordinate + * @param z the Z coordinate + * @param minY minimal height + * @param maxY maximal height + * @param filter a mask of blocks to consider, or null to consider any solid (movement-blocking) block + * @return height of highest block found or 'minY' + */ + public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) { for (int y = maxY; y >= minY; --y) { BlockVector3 pt = BlockVector3.at(x, y, z); - BlockState block = getBlock(pt); - if (block.getBlockType().getMaterial().isMovementBlocker()) { + if (filter == null + ? getBlock(pt).getBlockType().getMaterial().isMovementBlocker() + : filter.test(pt)) { return y; } } @@ -1237,7 +1252,7 @@ public class EditSession implements Extent, AutoCloseable { BlockVector3 to = region.getMinimumPoint(); // Remove the original blocks - com.sk89q.worldedit.function.pattern.Pattern pattern = replacement != null ? + Pattern pattern = replacement != null ? replacement : new BlockPattern(BlockTypes.AIR.getDefaultState()); BlockReplace remove = new BlockReplace(this, pattern); @@ -1882,12 +1897,25 @@ public class EditSession implements Extent, AutoCloseable { final Vector3 scaled = current.subtract(zero).divide(unit); try { - if (expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ(), defaultMaterial.getBlockType().getLegacyId(), 0) <= 0) { - // TODO data + int[] legacy = LegacyMapper.getInstance().getLegacyFromBlock(defaultMaterial.toImmutableState()); + int typeVar = 0; + int dataVar = 0; + if (legacy != null) { + typeVar = legacy[0]; + if (legacy.length > 1) { + dataVar = legacy[1]; + } + } + if (expression.evaluate(scaled.getX(), scaled.getY(), scaled.getZ(), typeVar, dataVar) <= 0) { return null; } - - return LegacyMapper.getInstance().getBlockFromLegacy((int) typeVariable.getValue(), (int) dataVariable.getValue()).toBaseBlock(); + int newType = (int) typeVariable.getValue(); + int newData = (int) dataVariable.getValue(); + if (newType != typeVar || newData != dataVar) { + return LegacyMapper.getInstance().getBlockFromLegacy((int) typeVariable.getValue(), (int) dataVariable.getValue()).toBaseBlock(); + } else { + return defaultMaterial; + } } catch (Exception e) { log.log(Level.WARNING, "Failed to create shape", e); return null; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index d1547b27e..0d1adc1d3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -42,6 +42,7 @@ import com.sk89q.worldedit.command.util.CreatureButcher; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; @@ -146,9 +147,9 @@ public class BrushCommands { BlockVector3 size = clipboard.getDimensions(); - worldEdit.checkMaxBrushRadius(size.getBlockX()); - worldEdit.checkMaxBrushRadius(size.getBlockY()); - worldEdit.checkMaxBrushRadius(size.getBlockZ()); + worldEdit.checkMaxBrushRadius(size.getBlockX() / 2D - 1); + worldEdit.checkMaxBrushRadius(size.getBlockY() / 2D - 1); + worldEdit.checkMaxBrushRadius(size.getBlockZ() / 2D - 1); BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); tool.setBrush(new ClipboardBrush(holder, ignoreAir, usingOrigin), "worldedit.brush.clipboard"); @@ -158,24 +159,24 @@ public class BrushCommands { @Command( aliases = { "smooth" }, - usage = "[size] [iterations]", + usage = "[size] [iterations] [filter]", desc = "Choose the terrain softener brush", help = - "Chooses the terrain softener brush.", + "Chooses the terrain softener brush. Optionally, specify a mask of blocks to be used for the heightmap.\n" + + "For example, '/brush smooth 2 4 grass_block,dirt,stone'.", min = 0, - max = 2 + max = 3 ) @CommandPermissions("worldedit.brush.smooth") public void smoothBrush(Player player, LocalSession session, EditSession editSession, - @Optional("2") double radius, @Optional("4") int iterations) throws WorldEditException { - + @Optional("2") double radius, @Optional("4") int iterations, @Optional Mask mask) throws WorldEditException { worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()); tool.setSize(radius); - tool.setBrush(new SmoothBrush(iterations), "worldedit.brush.smooth"); + tool.setBrush(new SmoothBrush(iterations, mask), "worldedit.brush.smooth"); - player.print(String.format("Smooth brush equipped (%.0f x %dx, using any block).", radius, iterations)); + player.print(String.format("Smooth brush equipped (%.0f x %dx, using %s).", radius, iterations, mask == null ? "any block" : "filter")); } @Command( 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 c12d897b8..00c70d5c6 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 @@ -240,17 +240,19 @@ public class RegionCommands { @Command( aliases = { "/smooth" }, - usage = "[iterations]", + usage = "[iterations] [filter]", desc = "Smooth the elevation in the selection", help = - "Smooths the elevation in the selection.", + "Smooths the elevation in the selection.\n" + + "Optionally, restricts the height map to a set of blocks specified with mask syntax.\n" + + "For example, '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain.", min = 0, - max = 1 + max = 2 ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) - public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations) throws WorldEditException { - HeightMap heightMap = new HeightMap(editSession, region); + public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Optional Mask mask) throws WorldEditException { + HeightMap heightMap = new HeightMap(editSession, region, mask); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); int affected = heightMap.applyFilter(filter, iterations); player.print("Terrain's height map smoothed. " + affected + " block(s) changed."); @@ -260,11 +262,12 @@ public class RegionCommands { @Command( aliases = { "/move" }, usage = "[count] [direction] [leave-id]", - flags = "s", + flags = "sa", desc = "Move the contents of the selection", help = "Moves the contents of the selection.\n" + "The -s flag shifts the selection to the target location.\n" + + "The -a flag skips air blocks.\n" + "Optionally fills the old location with .", min = 0, max = 3 @@ -276,9 +279,10 @@ public class RegionCommands { @Optional("1") @Range(min = 1) int count, @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, @Optional("air") Pattern replace, - @Switch('s') boolean moveSelection) throws WorldEditException { + @Switch('s') boolean moveSelection, + @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { - int affected = editSession.moveRegion(region, direction, count, true, replace); + int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); if (moveSelection) { try { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 3a4425f88..bd8ae4da9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -275,6 +275,7 @@ public class UtilityCommands { public void replaceNear(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int size = Math.max(1, args.getInteger(0)); + we.checkMaxRadius(size); int affected; Set from; Pattern to; @@ -288,9 +289,11 @@ public class UtilityCommands { if (args.argsLength() == 2) { from = null; + context.setRestricted(true); to = we.getPatternFactory().parseFromInput(args.getString(1), context); } else { from = we.getBlockFactory().parseFromListInput(args.getString(1), context); + context.setRestricted(true); to = we.getPatternFactory().parseFromInput(args.getString(2), context); } @@ -317,8 +320,8 @@ public class UtilityCommands { @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) public void snow(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + we.checkMaxRadius(size); int affected = editSession.simulateSnow(session.getPlacementPosition(player), size); player.print(affected + " surfaces covered. Let it snow~"); @@ -334,8 +337,8 @@ public class UtilityCommands { @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) public void thaw(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + we.checkMaxRadius(size); int affected = editSession.thaw(session.getPlacementPosition(player), size); player.print(affected + " surfaces thawed."); @@ -345,6 +348,7 @@ public class UtilityCommands { aliases = { "/green", "green" }, usage = "[radius]", desc = "Greens the area", + help = "Converts dirt to grass blocks. -f also converts coarse dirt.", flags = "f", min = 0, max = 1 @@ -352,8 +356,8 @@ public class UtilityCommands { @CommandPermissions("worldedit.green") @Logging(PLACEMENT) public void green(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - final double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; + we.checkMaxRadius(size); final boolean onlyNormalDirt = !args.hasFlag('f'); final int affected = editSession.green(session.getPlacementPosition(player), size, onlyNormalDirt); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java index d46b1ec3a..2ecf245d1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -31,12 +32,20 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import javax.annotation.Nullable; + public class SmoothBrush implements Brush { + private final Mask mask; private int iterations; public SmoothBrush(int iterations) { + this(iterations, null); + } + + public SmoothBrush(int iterations, @Nullable Mask mask) { this.iterations = iterations; + this.mask = mask; } @Override @@ -45,7 +54,7 @@ public class SmoothBrush implements Brush { Location min = new Location(editSession.getWorld(), posDouble.subtract(size, size, size)); BlockVector3 max = posDouble.add(size, size + 10, size).toBlockPoint(); Region region = new CuboidRegion(editSession.getWorld(), min.toVector().toBlockPoint(), max); - HeightMap heightMap = new HeightMap(editSession, region); + HeightMap heightMap = new HeightMap(editSession, region, mask); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); heightMap.applyFilter(filter, iterations); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index d6c62b4d1..4f3793adb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -238,7 +238,7 @@ public class DefaultBlockParser extends InputParser { } blockType = blockInHand.getBlockType(); - blockStates = blockInHand.getStates(); + blockStates.putAll(blockInHand.getStates()); } else if ("offhand".equalsIgnoreCase(typeString)) { // Get the block type from the item in the user's off hand. final BaseBlock blockInHand = getBlockInHand(context.requireActor(), HandSide.OFF_HAND); @@ -247,7 +247,7 @@ public class DefaultBlockParser extends InputParser { } blockType = blockInHand.getBlockType(); - blockStates = blockInHand.getStates(); + blockStates.putAll(blockInHand.getStates()); } else if ("pos1".equalsIgnoreCase(typeString)) { // Get the block type from the "primary position" final World world = context.requireWorld(); @@ -260,7 +260,7 @@ public class DefaultBlockParser extends InputParser { final BlockState blockInHand = world.getBlock(primaryPosition); blockType = blockInHand.getBlockType(); - blockStates = blockInHand.getStates(); + blockStates.putAll(blockInHand.getStates()); } else { // Attempt to lookup a block from ID or name. blockType = BlockTypes.get(typeString.toLowerCase()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index b3f663836..cbf73e857 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -248,9 +248,9 @@ public class ForwardExtentCopy implements Operation { } ExtentBlockCopy blockCopy = new ExtentBlockCopy(source, from, destination, to, currentTransform); - RegionMaskingFilter filter = new RegionMaskingFilter(sourceMask, blockCopy); - RegionFunction function = sourceFunction != null ? new CombinedRegionFunction(filter, sourceFunction) : filter; - RegionVisitor blockVisitor = new RegionVisitor(region, function); + RegionMaskingFilter filteredFunction = new RegionMaskingFilter(sourceMask, + sourceFunction == null ? blockCopy : new CombinedRegionFunction(blockCopy, sourceFunction)); + RegionVisitor blockVisitor = new RegionVisitor(region, filteredFunction); lastVisitor = blockVisitor; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/LayerVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/LayerVisitor.java index f3a191e12..f683637b3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/LayerVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/LayerVisitor.java @@ -99,7 +99,7 @@ public class LayerVisitor implements Operation { // Abort if we are underground if (function.isGround(column.toBlockVector3(maxY + 1))) { - return null; + continue; } boolean found = false; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index ff2dae979..c075bbf7f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -23,11 +23,14 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; +import javax.annotation.Nullable; + /** * Allows applications of Kernels onto the region's height map. * @@ -48,7 +51,7 @@ public class HeightMap { * @param session an edit session * @param region the region */ - public HeightMap(EditSession session, Region region) { + public HeightMap(EditSession session, Region region, @Nullable Mask mask) { checkNotNull(session); checkNotNull(region); @@ -67,7 +70,7 @@ public class HeightMap { data = new int[width * height]; for (int z = 0; z < height; ++z) { for (int x = 0; x < width; ++x) { - data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY); + data[z * width + x] = session.getHighestTerrainBlock(x + minX, z + minZ, minY, maxY, mask); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java index 2b1c49309..4601164f7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java @@ -58,7 +58,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment { @Override public int getBlockTypeAbs(double x, double y, double z) { - return editSession.getBlock(toWorld(x, y, z)).getBlockType().getLegacyId(); + return editSession.getBlock(BlockVector3.at(x, y, z)).getBlockType().getLegacyId(); } @Override @@ -68,7 +68,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment { @Override public int getBlockTypeRel(double x, double y, double z) { - return editSession.getBlock(toWorld(x, y, z)).getBlockType().getLegacyId(); + return editSession.getBlock(toWorldRel(x, y, z).toBlockPoint()).getBlockType().getLegacyId(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java index 8e95e9eb0..ea161111b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/Location.java @@ -208,6 +208,9 @@ public class Location { * @return the direction vector */ public Vector3 getDirection() { + if (Float.isNaN(getYaw()) && Float.isNaN(getPitch())) { + return Vector3.ZERO; + } double yaw = Math.toRadians(this.getYaw()); double pitch = Math.toRadians(this.getPitch()); double xz = Math.cos(pitch); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index 60c979dee..9d0cf2baa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -189,11 +189,16 @@ public class TargetBlock { public Location getAnyTargetBlockFace() { getAnyTargetBlock(); - return getCurrentBlock().setDirection(getCurrentBlock().toVector().subtract(getPreviousBlock().toVector())); + Location current = getCurrentBlock(); + if (current != null) + return current.setDirection(current.toVector().subtract(getPreviousBlock().toVector())); + else + return new Location(world, targetPos.toVector3(), Float.NaN, Float.NaN); } public Location getTargetBlockFace() { - getAnyTargetBlock(); + getTargetBlock(); + if (getCurrentBlock() == null) return null; return getCurrentBlock().setDirection(getCurrentBlock().toVector().subtract(getPreviousBlock().toVector())); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemRegistry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemRegistry.java index 3e0ae8800..1ab788e42 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemRegistry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/BundledItemRegistry.java @@ -32,7 +32,16 @@ public class BundledItemRegistry implements ItemRegistry { @Nullable @Override public String getName(ItemType itemType) { - BundledItemData.ItemEntry itemEntry = BundledItemData.getInstance().findById(itemType.getId()); - return itemEntry != null ? itemEntry.localizedName : null; + String id = itemType.getId(); + BundledItemData.ItemEntry itemEntry = BundledItemData.getInstance().findById(id); + if (itemEntry != null) { + String localized = itemEntry.localizedName; + if (localized.equals("Air")) { + int c = id.indexOf(':'); + return c < 0 ? id : id.substring(c + 1); + } + return localized; + } + return null; } }