From 7b47d9a9457a8a7a1292c502bbed0e266497cb50 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 23 May 2019 21:12:31 -0400 Subject: [PATCH] Add /tracemask. (#474) Allows setting a mask used for block traces. This allows brush tools to pass through various materials, such as water (e.g. `/tracemask #solid` or `/tracemask !air,water`) before starting to build. By default, a null mask is equivalent to #existing (original behavior). https://gfycat.com/ImmaculateFrayedCockatiel --- .../worldedit/bukkit/WorldEditListener.java | 17 +----- .../worldedit/command/ToolUtilCommands.java | 21 ++++++- .../worldedit/command/tool/BrushTool.java | 23 +++++++- .../worldedit/command/tool/DistanceWand.java | 9 +-- .../command/tool/LongRangeBuildTool.java | 11 +++- .../com/sk89q/worldedit/entity/Player.java | 22 +++++++ .../platform/AbstractPlayerActor.java | 22 ++++++- .../session/request/RequestExtent.java | 3 +- .../com/sk89q/worldedit/util/TargetBlock.java | 59 ++++++++++++++++--- 9 files changed, 150 insertions(+), 37 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index 15361fc10..69c803880 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -21,7 +21,6 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.util.StringUtil; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; @@ -33,7 +32,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -55,12 +53,6 @@ public class WorldEditListener implements Listener { private WorldEditPlugin plugin; - /** - * Called when a player plays an animation, such as an arm swing - * - * @param event Relevant event details - */ - /** * Construct the object; * @@ -112,12 +104,8 @@ public class WorldEditListener implements Listener { return; } - try { - if (event.getHand() == EquipmentSlot.OFF_HAND) { - return; // TODO api needs to be able to get either hand depending on event - // for now just ignore all off hand interacts - } - } catch (NoSuchMethodError | NoSuchFieldError ignored) { + if (event.getHand() == EquipmentSlot.OFF_HAND) { + return; } final Player player = plugin.wrapPlayer(event.getPlayer()); @@ -143,7 +131,6 @@ public class WorldEditListener implements Listener { event.setCancelled(true); } - } else if (action == Action.RIGHT_CLICK_BLOCK) { final Block clickedBlock = event.getClickedBlock(); final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index e053eccb8..2597372d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -51,7 +51,7 @@ public class ToolUtilCommands { @CommandPermissions("worldedit.superpickaxe") public void togglePickaxe(Player player, LocalSession session, @Arg(desc = "The new super pickaxe state", def = "") - Boolean superPickaxe) throws WorldEditException { + Boolean superPickaxe) { boolean hasSuperPickAxe = session.hasSuperPickAxe(); if (superPickaxe != null && superPickaxe == hasSuperPickAxe) { player.printError("Super pickaxe already " + (superPickaxe ? "enabled" : "disabled") + "."); @@ -75,11 +75,10 @@ public class ToolUtilCommands { public void mask(Player player, LocalSession session, @Arg(desc = "The mask to set", def = "") Mask mask) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask); if (mask == null) { - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(null); player.print("Brush mask disabled."); } else { - session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setMask(mask); player.print("Brush mask set."); } } @@ -122,4 +121,20 @@ public class ToolUtilCommands { session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size); player.print("Brush size set."); } + + @Command( + name = "tracemask", + desc = "Set the mask used to stop tool traces" + ) + @CommandPermissions("worldedit.brush.options.tracemask") + public void traceMask(Player player, LocalSession session, + @Arg(desc = "The trace mask to set", def = "") + Mask mask) throws WorldEditException { + session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setTraceMask(mask); + if (mask == null) { + player.print("Trace mask disabled."); + } else { + player.print("Trace mask set."); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index cb98b9a3a..2e324f9ee 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -34,7 +34,6 @@ import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Location; import javax.annotation.Nullable; @@ -47,6 +46,7 @@ public class BrushTool implements TraceTool { protected static int MAX_RANGE = 500; protected int range = -1; private Mask mask = null; + private Mask traceMask = null; private Brush brush = new SphereBrush(); @Nullable private Pattern material; @@ -86,6 +86,24 @@ public class BrushTool implements TraceTool { this.mask = filter; } + /** + * Get the mask used for identifying where to stop traces. + * + * @return the mask used to stop block traces + */ + public @Nullable Mask getTraceMask() { + return mask; + } + + /** + * Set the block mask used for identifying where to stop traces. + * + * @param traceMask the mask used to stop block traces + */ + public void setTraceMask(@Nullable Mask traceMask) { + this.traceMask = traceMask; + } + /** * Set the brush. * @@ -162,8 +180,7 @@ public class BrushTool implements TraceTool { @Override public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { - Location target = null; - target = player.getBlockTrace(getRange(), true); + Location target = player.getBlockTrace(getRange(), true, traceMask); if (target == null) { player.printError("No block in sight!"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java index 5fa95d04b..48d3dbf7a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; @@ -58,7 +59,6 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { } return false; - } @Override @@ -78,12 +78,13 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { return false; } - public Location getTarget(Player player) { + private Location getTarget(Player player) { Location target; + Mask mask = getTraceMask(); if (this.range > -1) { - target = player.getBlockTrace(getRange(), true); + target = player.getBlockTrace(getRange(), true, mask); } else { - target = player.getBlockTrace(MAX_RANGE); + target = player.getBlockTrace(MAX_RANGE, false, mask); } if (target == null) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java index 155beb4e8..27011c5ea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; @@ -91,8 +92,14 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo return false; } - public Location getTargetFace(Player player) { - Location target = player.getBlockTraceFace(getRange(), true); + private Location getTargetFace(Player player) { + Location target; + Mask mask = getTraceMask(); + if (this.range > -1) { + target = player.getBlockTrace(getRange(), true, mask); + } else { + target = player.getBlockTrace(MAX_RANGE, false, mask); + } if (target == null) { player.printError("No block in sight!"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java index d5be2e40d..3673d8842 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; @@ -210,6 +211,17 @@ public interface Player extends Entity, Actor { */ Location getBlockTrace(int range, boolean useLastBlock); + /** + * Get the point of the block being looked at. May return null. + * Will return the farthest away block before matching the stop mask if useLastBlock is true and no other block is found. + * + * @param range how far to checks for blocks + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return point + */ + Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the face that the player is looking at. * @@ -219,6 +231,16 @@ public interface Player extends Entity, Actor { */ Location getBlockTraceFace(int range, boolean useLastBlock); + /** + * Get the face that the player is looking at. + * + * @param range the range + * @param useLastBlock try to return the last valid block not matching the stop mask found + * @param stopMask the mask used to determine when to stop tracing + * @return a face + */ + Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask); + /** * Get the point of the block being looked at. May return null. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index e7aeac997..f51f992ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.NotABlockException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -41,6 +42,7 @@ import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; +import javax.annotation.Nullable; import java.io.File; /** @@ -323,13 +325,29 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { @Override public Location getBlockTrace(int range, boolean useLastBlock) { - TargetBlock tb = new TargetBlock(this, range, 0.2); - return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock()); + return getBlockTrace(range, useLastBlock, null); } @Override public Location getBlockTraceFace(int range, boolean useLastBlock) { + return getBlockTraceFace(range, useLastBlock, null); + } + + @Override + public Location getBlockTrace(int range, boolean useLastBlock, @Nullable Mask stopMask) { TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } + return (useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock()); + } + + @Override + public Location getBlockTraceFace(int range, boolean useLastBlock, @Nullable Mask stopMask) { + TargetBlock tb = new TargetBlock(this, range, 0.2); + if (stopMask != null) { + tb.setStopMask(stopMask); + } return (useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java index dc5aac815..ab47f7c51 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/request/RequestExtent.java @@ -45,7 +45,8 @@ public class RequestExtent implements Extent { if (request == null || !request.isValid()) { request = Request.request(); } - return request.getEditSession(); + final EditSession editSession = request.getEditSession(); + return editSession == null ? request.getWorld() : editSession; } @Override 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 9d0cf2baa..b887168c5 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 @@ -20,10 +20,15 @@ package com.sk89q.worldedit.util; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; + /** * This class uses an inefficient method to figure out what block a player * is looking towards. @@ -33,7 +38,8 @@ import com.sk89q.worldedit.world.World; */ public class TargetBlock { - private World world; + private final World world; + private int maxDistance; private double checkDistance, curDistance; private BlockVector3 targetPos = BlockVector3.ZERO; @@ -41,6 +47,11 @@ public class TargetBlock { private BlockVector3 prevPos = BlockVector3.ZERO; private Vector3 offset = Vector3.ZERO; + // the mask which dictates when to stop a trace - defaults to stopping at non-air blocks + private Mask stopMask; + // the mask which dictates when to stop a solid block trace - default to BlockMaterial#isMovementBlocker + private Mask solidMask; + /** * Constructor requiring a player, uses default values * @@ -50,6 +61,8 @@ public class TargetBlock { this.world = player.getWorld(); this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), 300, 1.65, 0.2); + this.stopMask = new ExistingBlockMask(world); + this.solidMask = new SolidBlockMask(world); } /** @@ -62,6 +75,36 @@ public class TargetBlock { public TargetBlock(Player player, int maxDistance, double checkDistance) { this.world = player.getWorld(); this.setValues(player.getLocation().toVector(), player.getLocation().getYaw(), player.getLocation().getPitch(), maxDistance, 1.65, checkDistance); + this.stopMask = new ExistingBlockMask(world); + this.solidMask = new SolidBlockMask(world); + } + + /** + * Set the mask used for determine where to stop traces. + * Setting to null will restore the default. + * + * @param stopMask the mask used to stop traces + */ + public void setStopMask(@Nullable Mask stopMask) { + if (stopMask == null) { + this.stopMask = new ExistingBlockMask(world); + } else { + this.stopMask = stopMask; + } + } + + /** + * Set the mask used for determine where to stop solid block traces. + * Setting to null will restore the default. + * + * @param solidMask the mask used to stop solid block traces + */ + public void setSolidMask(@Nullable Mask solidMask) { + if (solidMask == null) { + this.solidMask = new SolidBlockMask(world); + } else { + this.solidMask = solidMask; + } } /** @@ -79,7 +122,7 @@ public class TargetBlock { this.checkDistance = checkDistance; this.curDistance = 0; xRotation = (xRotation + 90) % 360; - yRotation = yRotation * -1; + yRotation *= -1; double h = (checkDistance * Math.cos(Math.toRadians(yRotation))); @@ -102,15 +145,15 @@ public class TargetBlock { boolean searchForLastBlock = true; Location lastBlock = null; while (getNextBlock() != null) { - if (world.getBlock(targetPos).getBlockType().getMaterial().isAir()) { + if (stopMask.test(targetPos)) { + break; + } else { if (searchForLastBlock) { lastBlock = getCurrentBlock(); if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { searchForLastBlock = false; } } - } else { - break; } } Location currentBlock = getCurrentBlock(); @@ -124,7 +167,8 @@ public class TargetBlock { * @return Block */ public Location getTargetBlock() { - while (getNextBlock() != null && world.getBlock(targetPos).getBlockType().getMaterial().isAir()) ; + //noinspection StatementWithEmptyBody + while (getNextBlock() != null && !stopMask.test(targetPos)) ; return getCurrentBlock(); } @@ -135,7 +179,8 @@ public class TargetBlock { * @return Block */ public Location getSolidTargetBlock() { - while (getNextBlock() != null && !world.getBlock(targetPos).getBlockType().getMaterial().isMovementBlocker()) ; + //noinspection StatementWithEmptyBody + while (getNextBlock() != null && !solidMask.test(targetPos)) ; return getCurrentBlock(); }