From 289ce500cc26b1491ea72a0625667ebcd03ec321 Mon Sep 17 00:00:00 2001 From: lipian Date: Thu, 17 Oct 2019 14:01:07 -0700 Subject: [PATCH 1/4] Updating the ink performers to work better --- .../com/thevoxelbox/voxelsniper/Message.java | 33 +- .../thevoxelbox/voxelsniper/SnipeData.java | 416 +++++++++--------- .../com/thevoxelbox/voxelsniper/Sniper.java | 14 +- .../voxelsniper/brush/PerformBrush.java | 155 ++++--- .../brush/shape/FillDownBrush.java | 2 +- .../voxelsniper/command/VoxelInkCommand.java | 74 ++-- .../command/VoxelInkReplaceCommand.java | 68 +-- .../voxelsniper/command/VoxelListCommand.java | 13 +- .../command/VoxelPerformerCommand.java | 25 +- .../command/VoxelReplaceCommand.java | 41 +- .../command/VoxelVoxelCommand.java | 41 +- .../voxelsniper/util/BlockHelper.java | 45 ++ .../voxelsniper/util/BlockTraitHelper.java | 74 ++++ 13 files changed, 570 insertions(+), 431 deletions(-) create mode 100644 src/main/java/com/thevoxelbox/voxelsniper/util/BlockTraitHelper.java diff --git a/src/main/java/com/thevoxelbox/voxelsniper/Message.java b/src/main/java/com/thevoxelbox/voxelsniper/Message.java index 6ffa2356..61795836 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/Message.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/Message.java @@ -24,6 +24,7 @@ */ package com.thevoxelbox.voxelsniper; +import com.thevoxelbox.voxelsniper.brush.PerformBrush; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.text.Text; @@ -62,7 +63,8 @@ public void brushName(String brushName) { * Display Center Parameter. */ public void center() { - this.snipeData.sendMessage(TextColors.DARK_BLUE, "Brush Center: ", TextColors.DARK_RED, this.snipeData.getCylinderCenter()); + this.snipeData.sendMessage(TextColors.DARK_BLUE, "Brush Center: ", + TextColors.DARK_RED, this.snipeData.getCylinderCenter()); } /** @@ -86,19 +88,38 @@ public void height() { } /** - * Display performer. + * Display performer data. * - * @param performerName + * @param placeMethod + * @param replaceMethod + * @param usePhysics */ - public void performerName(String performerName) { - this.snipeData.sendMessage(TextColors.DARK_PURPLE, "Performer: ", TextColors.DARK_GREEN, performerName); + public void performerData(PerformBrush.PerformerType placeMethod, + PerformBrush.PerformerType replaceMethod, + boolean usePhysics) { + String physics = usePhysics ? "On" : "Off"; + this.snipeData.sendMessage( + TextColors.DARK_PURPLE, + "Performers:", + TextColors.GREEN, + " place=", + TextColors.AQUA, + placeMethod, + TextColors.GREEN, + " replace=", + TextColors.AQUA, + replaceMethod, + TextColors.GREEN, + " physics=", + TextColors.AQUA, + physics); } /** * Display replace material. */ public void replace() { - this.snipeData.sendMessage(TextColors.AQUA, "Replace Material: ", TextColors.RED, this.snipeData.getReplaceId()); + this.snipeData.sendMessage(TextColors.DARK_BLUE, "Replace Material: ", TextColors.RED, this.snipeData.getReplaceId()); } /** diff --git a/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java b/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java index 1af1270a..1b67cf92 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java @@ -1,210 +1,206 @@ -/* - * This file is part of VoxelSniper, licensed under the MIT License (MIT). - * - * Copyright (c) The VoxelBox - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.thevoxelbox.voxelsniper; - -import com.thevoxelbox.voxelsniper.util.VoxelList; - -import org.spongepowered.api.Sponge; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.data.key.Key; -import org.spongepowered.api.data.value.BaseValue; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.world.World; - -public class SnipeData { - - private Sniper owner; - private Message voxelMessage; - - private double brushSize; - private BlockState voxelState; - private BlockState replaceState; - private VoxelList voxelList; - private Key voxelInkKey; - private Object voxelInkValue; - private Key replaceInkKey; - private Object replaceInkValue; - - private int voxelHeight; - private int cylinderCenter; - private int range; - private boolean ranged; - private boolean lightning; - - public SnipeData(Sniper vs) { - this.owner = vs; - - this.voxelState = Sponge.getRegistry().getType(BlockState.class, VoxelSniperConfiguration.DEFAULT_VOXEL_ID) - .orElse(BlockTypes.AIR.getDefaultState()); - this.replaceState = Sponge.getRegistry().getType(BlockState.class, VoxelSniperConfiguration.DEFAULT_REPLACE_ID) - .orElse(BlockTypes.AIR.getDefaultState()); - - this.brushSize = VoxelSniperConfiguration.DEFAULT_BRUSH_SIZE; - this.voxelList = new VoxelList(); - this.voxelHeight = VoxelSniperConfiguration.DEFAULT_VOXEL_HEIGHT; - this.cylinderCenter = VoxelSniperConfiguration.DEFAULT_CYLINDER_CENTER; - this.range = 0; - - this.ranged = false; - this.lightning = false; - - this.voxelInkKey = null; - this.voxelInkValue = null; - this.replaceInkKey = null; - this.replaceInkValue = null; - } - - public double getBrushSize() { - return this.brushSize; - } - - public void setBrushSize(double brushSize) { - this.brushSize = brushSize; - } - - public int getCylinderCenter() { - return this.cylinderCenter; - } - - public void setCylinderCenter(int cCen) { - this.cylinderCenter = cCen; - } - - public int getRange() { - return this.range; - } - - public void setRange(int range) { - this.range = range; - } - - public String getReplaceId() { - return this.replaceState.getId(); - } - - public BlockState getReplaceState() { - return this.replaceState; - } - - public void setReplaceState(BlockState state) { - this.replaceState = state; - } - - public int getVoxelHeight() { - return this.voxelHeight; - } - - public void setVoxelHeight(int voxelHeight) { - this.voxelHeight = voxelHeight; - } - - public String getVoxelId() { - return voxelState.getId(); - } - - public BlockState getVoxelState() { - return this.voxelState; - } - - public void setVoxelState(BlockState state) { - this.voxelState = state; - } - - public VoxelList getVoxelList() { - return this.voxelList; - } - - public void setVoxelList(VoxelList voxelList) { - this.voxelList = voxelList; - } - - public Message getVoxelMessage() { - return this.voxelMessage; - } - - public void setVoxelMessage(Message voxelMessage) { - this.voxelMessage = voxelMessage; - } - - public World getWorld() { - return this.owner.getPlayer().getWorld(); - } - - public boolean isLightningEnabled() { - return this.lightning; - } - - public void setLightningEnabled(boolean lightning) { - this.lightning = lightning; - } - - public boolean isRanged() { - return this.ranged; - } - - public void setRanged(boolean ranged) { - this.ranged = ranged; - } - - public Key> getVoxelInkKey() { - return this.voxelInkKey; - } - - public Object getVoxelInkValue() { - return this.voxelInkValue; - } - - public , T> void setVoxelInk(Key key, T value) { - this.voxelInkKey = key; - this.voxelInkValue = value; - } - - public Key> getReplaceInkKey() { - return this.replaceInkKey; - } - - public Object getReplaceInkValue() { - return this.replaceInkValue; - } - - public , T> void setReplaceInk(Key key, T value) { - this.replaceInkKey = key; - this.replaceInkValue = value; - } - - public Sniper owner() { - return this.owner; - } - - - public void sendMessage(Object... args) { - this.owner.getPlayer().sendMessage(Text.of(args)); - } - - public void sendMessage(Text msg) { - this.owner.getPlayer().sendMessage(msg); - } -} +/* + * This file is part of VoxelSniper, licensed under the MIT License (MIT). + * + * Copyright (c) The VoxelBox + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.thevoxelbox.voxelsniper; + +import com.thevoxelbox.voxelsniper.util.VoxelList; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.block.trait.BlockTrait; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.world.World; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +public class SnipeData { + + private Sniper owner; + private Message voxelMessage; + + private double brushSize; + private BlockState voxelState; + private BlockState replaceState; + private VoxelList voxelList; + private Map, Object> voxelInkTraits; + private Map, Object> replaceInkTraits; + + private int voxelHeight; + private int cylinderCenter; + private int range; + private boolean ranged; + private boolean lightning; + + public SnipeData(Sniper vs) { + this.owner = vs; + + this.voxelState = Sponge.getRegistry().getType(BlockState.class, VoxelSniperConfiguration.DEFAULT_VOXEL_ID) + .orElse(BlockTypes.AIR.getDefaultState()); + this.replaceState = Sponge.getRegistry().getType(BlockState.class, VoxelSniperConfiguration.DEFAULT_REPLACE_ID) + .orElse(BlockTypes.AIR.getDefaultState()); + + this.brushSize = VoxelSniperConfiguration.DEFAULT_BRUSH_SIZE; + this.voxelList = new VoxelList(); + this.voxelHeight = VoxelSniperConfiguration.DEFAULT_VOXEL_HEIGHT; + this.cylinderCenter = VoxelSniperConfiguration.DEFAULT_CYLINDER_CENTER; + this.range = 0; + + voxelInkTraits = new HashMap<>(); + replaceInkTraits = new HashMap<>(); + } + + public double getBrushSize() { + return this.brushSize; + } + + public void setBrushSize(double brushSize) { + this.brushSize = brushSize; + } + + public int getCylinderCenter() { + return this.cylinderCenter; + } + + public void setCylinderCenter(int cCen) { + this.cylinderCenter = cCen; + } + + public int getRange() { + return this.range; + } + + public void setRange(int range) { + this.range = range; + } + + public String getReplaceId() { + return this.replaceState.getId(); + } + + public BlockState getReplaceState() { + return this.replaceState; + } + + public void setReplaceState(BlockState state) { + this.replaceState = state; + this.replaceInkTraits.clear(); + this.replaceInkTraits.putAll(state.getTraitMap()); + } + + public int getVoxelHeight() { + return this.voxelHeight; + } + + public void setVoxelHeight(int voxelHeight) { + this.voxelHeight = voxelHeight; + } + + public String getVoxelId() { + return this.voxelState.getId(); + } + + public BlockState getVoxelState() { + return this.voxelState; + } + + public void setVoxelState(BlockState state) { + this.voxelState = state; + this.voxelInkTraits.clear(); + this.voxelInkTraits.putAll(state.getTraitMap()); + } + + public VoxelList getVoxelList() { + return voxelList; + } + + public void setVoxelList(VoxelList voxelList) { + this.voxelList = voxelList; + } + + public Message getVoxelMessage() { + return voxelMessage; + } + + public void setVoxelMessage(Message voxelMessage) { + this.voxelMessage = voxelMessage; + } + + public World getWorld() { + return owner.getPlayer().getWorld(); + } + + public boolean isLightningEnabled() { + return lightning; + } + + public void setLightningEnabled(boolean lightning) { + this.lightning = lightning; + } + + public boolean isRanged() { + return ranged; + } + + public void setRanged(boolean ranged) { + this.ranged = ranged; + } + + public Map, Object> getVoxelInkTraits() { + return Collections.unmodifiableMap(voxelInkTraits); + } + + // Attempts to add the traitValues as the current placement ink. If the trait values can't be used with the current + // voxel state, no changes are made. + public void setVoxelInkTraits(Map, Object> traitValues) { + this.voxelInkTraits.clear(); + this.voxelInkTraits.putAll(traitValues); + } + + public Map, Object> getReplaceInkTraits() { + return Collections.unmodifiableMap(replaceInkTraits); + } + + // Attempts to add the traitValues as the current replacement ink. If the trait values can't be used with the + // current replacement state, no changes are made. + public void setReplaceInkTraits(Map, Object> traitValues) { + this.replaceInkTraits.clear(); + this.replaceInkTraits.putAll(traitValues); + } + + public Sniper owner() { + return this.owner; + } + + + public void sendMessage(Object... args) { + this.owner.getPlayer().sendMessage(Text.of(args)); + } + + public void sendMessage(Text msg) { + this.owner.getPlayer().sendMessage(msg); + } +} diff --git a/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java b/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java index 9745a5cd..21ddaf10 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/Sniper.java @@ -48,6 +48,7 @@ import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; +import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.Map; import java.util.UUID; @@ -129,8 +130,7 @@ public boolean snipe(InteractionType action, ItemStack itemInHand) { } SnipeData snipeData = sniperTool.getSnipeData(); - if (player.get(Keys.IS_SNEAKING).orElse(false)) { - Location targetBlock = null; + if (player.getOrElse(Keys.IS_SNEAKING, false)) { SnipeAction snipeAction = sniperTool.getActionAssigned(itemInHand.getType()); Predicate> filter = BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1); @@ -139,6 +139,7 @@ public boolean snipe(InteractionType action, ItemStack itemInHand) { rayBuilder.distanceLimit(snipeData.getRange()); } BlockRay ray = rayBuilder.build(); + Location targetBlock = null; while (ray.hasNext()) { targetBlock = ray.next().getLocation(); } @@ -436,10 +437,11 @@ public Brush previousBrush() { private Brush instanciateBrush(Class brush) { // TODO: Check errors here and report. try { - return brush.newInstance(); - } catch (InstantiationException e) { - return null; - } catch (IllegalAccessException e) { + return brush.getConstructor().newInstance(); + } catch (InstantiationException | + IllegalAccessException | + NoSuchMethodException | + InvocationTargetException e) { return null; } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java index 8c2ccb75..909279b3 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java @@ -30,68 +30,67 @@ import com.thevoxelbox.voxelsniper.Message; import com.thevoxelbox.voxelsniper.SnipeData; +import com.thevoxelbox.voxelsniper.util.BlockHelper; import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.data.key.Key; +import org.spongepowered.api.world.BlockChangeFlag; import org.spongepowered.api.world.BlockChangeFlags; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import java.util.Arrays; import java.util.Optional; +import java.util.regex.Matcher; import java.util.regex.Pattern; public abstract class PerformBrush extends Brush { - private static final Pattern PERFORMER = Pattern.compile("[mMcC][mMcC]?[pP]?"); + private static final Pattern PERFORMER = Pattern.compile("([mic])([mic]?)(p?)"); - protected PerformerType place = PerformerType.TYPE; - protected PerformerType replace = PerformerType.NONE; - protected boolean physics = true; + protected PerformerType placeMethod = PerformerType.TYPE; + protected PerformerType replaceMethod = PerformerType.NONE; + protected boolean usePhysics = true; public void parse(String[] args, SnipeData v) { - String handle = args[0]; - if (PERFORMER.matcher(handle).matches()) { - char p = handle.charAt(0); - if (p == 'm' || p == 'M') { - this.place = PerformerType.TYPE; - } else if (p == 'c' || p == 'C') { - this.place = PerformerType.COMBO; - } - int i = 1; - if (handle.length() >= 2) { - char r = handle.charAt(i); - i = 2; - if (r == 'm' || r == 'M') { - this.replace = PerformerType.TYPE; - } else if (r == 'c' || r == 'C') { - this.replace = PerformerType.COMBO; - } else { - i = 1; - this.replace = PerformerType.NONE; - } - } - if (handle.length() >= i + 1) { - char e = handle.charAt(i); - if (e == 'p' || e == 'P') { - this.physics = false; - } else { - this.physics = true; - } + String handle = args[0].toLowerCase(); + Matcher performersMatch = PERFORMER.matcher(handle); + + if (performersMatch.matches()) { + placeMethod = stringToMethod(performersMatch.group(1)); + if (placeMethod == PerformerType.NONE) { + throw new IllegalArgumentException("Unknown placement method '" + performersMatch.group(1) + "'"); } + + replaceMethod = stringToMethod(performersMatch.group(2)); + usePhysics = !performersMatch.group(3).equals("p"); + parameters(Arrays.copyOfRange(args, 1, args.length), v); } else { parameters(args, v); } + + v.getVoxelMessage().performerData(placeMethod, replaceMethod, usePhysics); + } + + private PerformerType stringToMethod(String rawMethod) { + switch (rawMethod) { + case "m": + return PerformerType.TYPE; + case "i": + return PerformerType.TRAITS; + case "c": + return PerformerType.COMBO; + } + return PerformerType.NONE; } public void showInfo(Message vm) { - String name = this.place.name().toLowerCase(); - if (this.replace != PerformerType.NONE) { - name += " " + this.replace.name().toLowerCase(); + String name = placeMethod.name().toLowerCase(); + if (replaceMethod != PerformerType.NONE) { + name += replaceMethod.name().toLowerCase(); } - vm.performerName(name); + vm.performerData(placeMethod, replaceMethod, usePhysics); vm.voxel(); - if (this.replace != PerformerType.NONE) { + if (replaceMethod != PerformerType.NONE) { vm.replace(); } } @@ -104,54 +103,80 @@ protected boolean perform(SnipeData v, int x, int y, int z) { if (y < 0 || y >= Brush.WORLD_HEIGHT) { return false; } - if (this.replace != PerformerType.NONE) { - BlockState current = this.world.getBlock(x, y, z); - switch (this.replace) { + + BlockState current = this.world.getBlock(x, y, z); + switch (replaceMethod) { case TYPE: +<<<<<<< HEAD if (current.getType() != v.getReplaceState().getType()) { +======= + if (!sameBlockType(current, v.getReplaceState())) { +>>>>>>> 8e15cbd... Updating the ink performers to work better return false; } break; - case STATE: - // @Todo filter by key and value + case TRAITS: + if (!BlockHelper.hasTraits(current, v.getReplaceInkTraits())) { + return false; + } break; case COMBO: +<<<<<<< HEAD if (current != v.getReplaceState()) { +======= + if (!sameBlockType(current, v.getReplaceState()) || + !BlockHelper.hasTraits(current, v.getReplaceInkTraits())) { +>>>>>>> 8e15cbd... Updating the ink performers to work better return false; } break; case NONE: default: break; - } } - switch (this.place) { - case TYPE: - setBlockType(x, y, z, v.getVoxelState().getType(), this.physics ? BlockChangeFlags.ALL : BlockChangeFlags.NONE); - break; - case STATE: - BlockState current = this.world.getBlock(x, y, z); - @SuppressWarnings({"unchecked", "rawtypes"}) - Optional place = current.with((Key) v.getVoxelInkKey(), v.getVoxelInkValue()); - if (!place.isPresent()) { - return false; - } - setBlockState(x, y, z, place.get(), this.physics ? BlockChangeFlags.ALL : BlockChangeFlags.NONE); - break; - case COMBO: - setBlockState(x, y, z, v.getVoxelState(), this.physics ? BlockChangeFlags.ALL : BlockChangeFlags.NONE); - break; - case NONE: - default: - throw new IllegalStateException("Unsupported place type " + this.place.name()); + + BlockChangeFlag physicsFlags = usePhysics ? BlockChangeFlags.ALL : BlockChangeFlags.NONE; + switch (placeMethod) { + case TYPE: + setBlockType(x, y, z, v.getVoxelState().getType(), physicsFlags); + break; + case TRAITS: + BlockState place = BlockHelper.addTraits(current, v.getVoxelInkTraits()); + setBlockState(x, y, z, place, physicsFlags); + break; + case COMBO: + setBlockState(x, y, z, v.getVoxelState(), physicsFlags); + break; + case NONE: + default: + throw new IllegalStateException("Unsupported place type " + placeMethod.name()); } return true; } - public static enum PerformerType { + private boolean sameBlockType(BlockState a, BlockState b) { + return a.getType().equals(b.getType()); + } + + public enum PerformerType { TYPE, - STATE, + TRAITS, COMBO, NONE; + + public String toString() { + switch (this) { + case TRAITS: + return "Ink"; + case TYPE: + return "Material"; + case COMBO: + return "Combo"; + case NONE: + return "None"; + } + + return "Unkown"; + } } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/brush/shape/FillDownBrush.java b/src/main/java/com/thevoxelbox/voxelsniper/brush/shape/FillDownBrush.java index b1bf5861..99147de0 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/brush/shape/FillDownBrush.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/brush/shape/FillDownBrush.java @@ -79,7 +79,7 @@ private void fillDown(SnipeData v, Location targetBlock) { } } for (; y >= 0; y--) { - if (this.replace != PerformerType.NONE) { + if (replaceMethod != PerformerType.NONE) { if (!perform(v, x, y, z)) { break; } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkCommand.java index 13ea2f72..d18dc433 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkCommand.java @@ -24,11 +24,10 @@ */ package com.thevoxelbox.voxelsniper.command; -import com.thevoxelbox.voxelsniper.Sniper; -import com.thevoxelbox.voxelsniper.SniperManager; -import com.thevoxelbox.voxelsniper.VoxelSniperConfiguration; +import com.thevoxelbox.voxelsniper.*; +import com.thevoxelbox.voxelsniper.util.BlockTraitHelper; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandException; +import org.spongepowered.api.block.trait.BlockTrait; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.command.args.CommandContext; @@ -37,45 +36,56 @@ import org.spongepowered.api.command.spec.CommandSpec; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.format.TextColors; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; public class VoxelInkCommand implements CommandExecutor { public static void setup(Object plugin) { Sponge.getCommandManager().register(plugin, CommandSpec.builder() - .arguments(GenericArguments.playerOrSource(Text.of("sniper")), GenericArguments.string(Text.of("key")), - GenericArguments.literal(Text.of("equals"), "="), GenericArguments.string(Text.of("value"))) - .executor(new VoxelInkCommand()).permission(VoxelSniperConfiguration.PERMISSION_SNIPER) - .description(Text.of("VoxelSniper Ink selection")).build(), "vi"); + .arguments(GenericArguments.playerOrSource(Text.of("sniper")), + GenericArguments.allOf( + GenericArguments.string(Text.of("key=value")) + )) + .executor(new VoxelInkCommand()) + .permission(VoxelSniperConfiguration.PERMISSION_SNIPER) + .description(usageText()) + .build(), "vi"); } @Override - public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { + public CommandResult execute(CommandSource src, CommandContext gargs) { Player player = (Player) gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); + SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); + + Collection keyValues = gargs.getAll("key=value"); + if (keyValues.size() == 0) { + src.sendMessage(usageText()); + return CommandResult.success(); + } + + Optional, Object>> optInkTraits = + BlockTraitHelper.parseKeyValues(keyValues, snipeData.getVoxelState(), src); + if (optInkTraits.isPresent()) { + snipeData.setVoxelInkTraits(optInkTraits.get()); + snipeData.getVoxelMessage().voxel(); + } - String key = (String) gargs.getOne("key").get(); - String value = (String) gargs.getOne("value").get(); - - // @Spongify turn these into a key and value for a blockstate -// if (args.length == 0) { -// Block targetBlock = new RangeBlockHelper(player, player.getWorld()).getTargetBlock(); -// if (targetBlock != null) { -// dataValue = targetBlock.getData(); -// } else { -// return true; -// } -// } else { -// try { -// dataValue = Byte.parseByte(args[0]); -// } catch (NumberFormatException exception) { -// player.sendMessage("Couldn't parse input."); -// return true; -// } -// } -// -// SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); -// snipeData.setData(dataValue); -// snipeData.getVoxelMessage().data(); return CommandResult.success(); } + + private static Text usageText() { + return Text.of( + TextColors.RED, + "Voxel ink selection\n" + + "Pass one or more block property in the form key=value separated by a space. " + + "These properties will be applied to blocks when placing them. E.g, passing " + + "color=blue when your voxel is wool will place blue wool. Voxel ink won't " + + "be used unless you set the place method to ink or combo." + ); + } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkReplaceCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkReplaceCommand.java index cd2eb2d6..9f04c79b 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkReplaceCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelInkReplaceCommand.java @@ -24,10 +24,13 @@ */ package com.thevoxelbox.voxelsniper.command; +import com.thevoxelbox.voxelsniper.SnipeData; import com.thevoxelbox.voxelsniper.Sniper; import com.thevoxelbox.voxelsniper.SniperManager; import com.thevoxelbox.voxelsniper.VoxelSniperConfiguration; +import com.thevoxelbox.voxelsniper.util.BlockTraitHelper; import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.trait.BlockTrait; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandSource; @@ -37,13 +40,20 @@ import org.spongepowered.api.command.spec.CommandSpec; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.format.TextColors; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; public class VoxelInkReplaceCommand implements CommandExecutor { public static void setup(Object plugin) { Sponge.getCommandManager().register(plugin, CommandSpec.builder() - .arguments(GenericArguments.playerOrSource(Text.of("sniper")), GenericArguments.string(Text.of("key")), - GenericArguments.literal(Text.of("equals"), "="), GenericArguments.string(Text.of("value"))) + .arguments(GenericArguments.playerOrSource(Text.of("sniper")), + GenericArguments.allOf( + GenericArguments.string(Text.of("key=value")) + )) .executor(new VoxelInkReplaceCommand()).permission(VoxelSniperConfiguration.PERMISSION_SNIPER) .description(Text.of("VoxelSniper Replace Ink selection")).build(), "vir"); } @@ -52,39 +62,31 @@ public static void setup(Object plugin) { public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { Player player = (Player) gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); + SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); - String key = (String) gargs.getOne("key").get(); - String value = (String) gargs.getOne("value").get(); + Collection keyValues = gargs.getAll("key=value"); + if (keyValues.size() == 0) { + src.sendMessage(usageText()); + return CommandResult.success(); + } - // @Spongify turn these into a key and value for block state -// if (args.length == 0) -// { -// Block targetBlock = new RangeBlockHelper(player, player.getWorld()).getTargetBlock(); -// if (targetBlock != null) -// { -// dataValue = targetBlock.getData(); -// } -// else -// { -// return true; -// } -// } -// else -// { -// try -// { -// dataValue = Byte.parseByte(args[0]); -// } -// catch (NumberFormatException exception) -// { -// player.sendMessage("Couldn't parse input."); -// return true; -// } -// } -// -// SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); -// snipeData.setReplaceData(dataValue); -// snipeData.getVoxelMessage().replaceData(); + Optional, Object>> optInkTraits = + BlockTraitHelper.parseKeyValues(keyValues, snipeData.getReplaceState(), src); + if (optInkTraits.isPresent()) { + snipeData.setReplaceInkTraits(optInkTraits.get()); + snipeData.getVoxelMessage().replace(); + } return CommandResult.success(); } + + private static Text usageText() { + return Text.of( + TextColors.RED, + "Voxel replace ink selection\n" + + "Pass one or more block property in the form key=value separated by a space. " + + "These properties will used to check blocks when replacing them. E.g, passing " + + "color=blue will only replace blocks with blue color attribute. To use the " + + "replace ink make sure you set the replace method to ink or combo." + ); + } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java index acadbe04..c1f012ea 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java @@ -28,6 +28,7 @@ import com.thevoxelbox.voxelsniper.Sniper; import com.thevoxelbox.voxelsniper.SniperManager; import com.thevoxelbox.voxelsniper.VoxelSniperConfiguration; +import com.thevoxelbox.voxelsniper.util.BlockHelper; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; @@ -41,10 +42,6 @@ import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.Text; import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.util.blockray.BlockRay; -import org.spongepowered.api.util.blockray.BlockRay.BlockRayBuilder; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.World; import java.util.Optional; @@ -67,13 +64,7 @@ public CommandResult execute(CommandSource src, CommandContext gargs) throws Com Optional oargs = gargs.getOne("args"); SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); if (!oargs.isPresent()) { - Location targetBlock = null; - BlockRayBuilder rayBuilder = BlockRay.from(player).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)); - BlockRay ray = rayBuilder.build(); - while (ray.hasNext()) { - targetBlock = ray.next().getLocation(); - } - snipeData.getVoxelList().add(targetBlock.getBlock()); + snipeData.getVoxelList().add(BlockHelper.stateOrWhereLooking(Optional.empty(), player).get()); snipeData.getVoxelMessage().voxelList(); return CommandResult.success(); } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelPerformerCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelPerformerCommand.java index d8419184..08fc7d55 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelPerformerCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelPerformerCommand.java @@ -48,13 +48,13 @@ public static void setup(Object plugin) { Sponge.getCommandManager().register(plugin, CommandSpec.builder().arguments(GenericArguments.playerOrSource(Text.of("sniper")), GenericArguments.string(Text.of("performer"))) .executor(new VoxelPerformerCommand()).permission(VoxelSniperConfiguration.PERMISSION_SNIPER) - .description(Text.of("VoxelSniper performer selection")).build(), + .description(usageText()).build(), "p"); } @Override public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { - Player player = (Player) gargs.getOne("sniper").get(); + Player player = gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); @@ -62,10 +62,29 @@ public CommandResult execute(CommandSource src, CommandContext gargs) throws Com Brush brush = sniper.getBrush(sniper.getCurrentToolId()); if (brush instanceof PerformBrush) { - ((PerformBrush) brush).parse(new String[] { performer }, snipeData); + PerformBrush pBrush = (PerformBrush) brush; + pBrush.parse(new String[] { performer }, snipeData); } else { player.sendMessage(Text.of(TextColors.RED, "This brush is not a performer brush.")); } return CommandResult.success(); } + + private static Text usageText() { + return Text.of( + TextColors.RED, + "Performer takes in a string of up to three characters. The first\n" + + "is required and describes the placement method. The second describes\n" + + "the replacement method and is optional. The third letter must be a p\n" + + "and if present means that physics will not be applied when placing\n" + + "blocks. The either two must be one of: \n\n" + + " - m for material. Placing/replacing is solely done based on the base\n" + + " block you have selected.\n\n" + + " - i for ink. Blocks in the affected area will only have their properties\n" + + " changed when placing or will only be replace if their properties match\n" + + " the current ink\n\n" + + " - c for combo. As the name suggests, placement and replacement will use\n" + + " both the block type and ink values for placing/replacing." + ); + } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelReplaceCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelReplaceCommand.java index 71cd0a49..c29d88d0 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelReplaceCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelReplaceCommand.java @@ -28,9 +28,9 @@ import com.thevoxelbox.voxelsniper.Sniper; import com.thevoxelbox.voxelsniper.SniperManager; import com.thevoxelbox.voxelsniper.VoxelSniperConfiguration; +import com.thevoxelbox.voxelsniper.util.BlockHelper; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockType; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandSource; @@ -41,10 +41,6 @@ import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.Text; import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.util.blockray.BlockRay; -import org.spongepowered.api.util.blockray.BlockRay.BlockRayBuilder; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.World; import java.util.Optional; @@ -62,38 +58,19 @@ public static void setup(Object plugin) { @Override public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { - Player player = (Player) gargs.getOne("sniper").get(); + Player player = gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); Optional material = gargs.getOne("material"); - if (!material.isPresent()) { - Location targetBlock = null; - BlockRayBuilder rayBuilder = BlockRay.from(player).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)); - BlockRay ray = rayBuilder.build(); - while (ray.hasNext()) { - targetBlock = ray.next().getLocation(); - } - snipeData.setReplaceState(targetBlock.getBlock()); - snipeData.getVoxelMessage().replace(); + + Optional newState = BlockHelper.stateOrWhereLooking(material, player); + if (!newState.isPresent() && material.isPresent()) { + src.sendMessage(Text.of(TextColors.RED, "Unknown block ", material.get())); return CommandResult.success(); } - String materialName = material.get(); - if (!materialName.contains(":")) { - materialName = "minecraft:" + materialName; - } - Optional type = Sponge.getRegistry().getType(BlockType.class, materialName); - if (type.isPresent()) { - snipeData.setReplaceState(type.get().getDefaultState()); - snipeData.getVoxelMessage().replace(); - } else { - Optional state = Sponge.getRegistry().getType(BlockState.class, materialName); - if (state.isPresent()) { - snipeData.setReplaceState(state.get()); - snipeData.getVoxelMessage().replace(); - } else { - player.sendMessage(Text.of(TextColors.RED, "Material not found.")); - } - } + snipeData.setReplaceState(newState.get()); + snipeData.getVoxelMessage().replace(); + return CommandResult.success(); } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelVoxelCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelVoxelCommand.java index f7cd984c..53304bd4 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelVoxelCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelVoxelCommand.java @@ -28,9 +28,9 @@ import com.thevoxelbox.voxelsniper.Sniper; import com.thevoxelbox.voxelsniper.SniperManager; import com.thevoxelbox.voxelsniper.VoxelSniperConfiguration; +import com.thevoxelbox.voxelsniper.util.BlockHelper; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockType; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.command.CommandSource; @@ -41,10 +41,6 @@ import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.Text; import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.util.blockray.BlockRay; -import org.spongepowered.api.util.blockray.BlockRay.BlockRayBuilder; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.World; import java.util.Optional; @@ -62,38 +58,19 @@ public static void setup(Object plugin) { @Override public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { - Player player = (Player) gargs.getOne("sniper").get(); + Player player = gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); Optional material = gargs.getOne("material"); - if (!material.isPresent()) { - Location targetBlock = null; - BlockRayBuilder rayBuilder = BlockRay.from(player).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)); - BlockRay ray = rayBuilder.build(); - while (ray.hasNext()) { - targetBlock = ray.next().getLocation(); - } - snipeData.setVoxelState(targetBlock.getBlock()); - snipeData.getVoxelMessage().voxel(); + + Optional newState = BlockHelper.stateOrWhereLooking(material, player); + if (!newState.isPresent() && material.isPresent()) { + src.sendMessage(Text.of(TextColors.RED, "Unknown block ", material.get())); return CommandResult.success(); } - String materialName = material.get(); - if (!materialName.contains(":")) { - materialName = "minecraft:" + materialName; - } - Optional type = Sponge.getRegistry().getType(BlockType.class, materialName); - if (type.isPresent()) { - snipeData.setVoxelState(type.get().getDefaultState()); - snipeData.getVoxelMessage().voxel(); - } else { - Optional state = Sponge.getRegistry().getType(BlockState.class, materialName); - if (state.isPresent()) { - snipeData.setVoxelState(state.get()); - snipeData.getVoxelMessage().voxel(); - } else { - player.sendMessage(Text.of(TextColors.RED, "Material not found.")); - } - } + snipeData.setVoxelState(newState.get()); + snipeData.getVoxelMessage().voxel(); + return CommandResult.success(); } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java index c555cda7..bb71a1fc 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java @@ -24,11 +24,18 @@ */ package com.thevoxelbox.voxelsniper.util; +import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.trait.BlockTrait; import org.spongepowered.api.data.property.block.MatterProperty; import org.spongepowered.api.data.property.block.MatterProperty.Matter; import org.spongepowered.api.data.property.block.SolidCubeProperty; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.util.blockray.BlockRay; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; +import java.util.Map; import java.util.Optional; public class BlockHelper { @@ -63,4 +70,42 @@ public static boolean isSolid(BlockState state) { return false; } + public static boolean hasTraits(BlockState state, Map, Object> traits) { + for (Map.Entry, ?> trait : state.getTraitMap().entrySet()) { + Object expectedValue = traits.get(trait.getKey()); + if (!trait.getValue().equals(expectedValue)) { + return false; + } + } + + return true; + } + + public static BlockState addTraits(BlockState state, Map, Object> traits) { + Optional nextState; + for (Map.Entry, Object> traitEntry : traits.entrySet()) { + nextState = state.withTrait(traitEntry.getKey(), traitEntry.getValue()); + if (nextState.isPresent()) { + state = nextState.get(); + } + } + + return state; + } + + public static Optional stateOrWhereLooking(Optional rawState, Player player) { + if (rawState.isPresent()) { + return Sponge.getRegistry().getType(BlockState.class, rawState.get()); + } + + Location targetBlock = null; + BlockRay.BlockRayBuilder rayBuilder = + BlockRay.from(player).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)); + BlockRay ray = rayBuilder.build(); + while (ray.hasNext()) { + targetBlock = ray.next().getLocation(); + } + + return Optional.of(targetBlock.getBlock()); + } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/util/BlockTraitHelper.java b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockTraitHelper.java new file mode 100644 index 00000000..c60e72ed --- /dev/null +++ b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockTraitHelper.java @@ -0,0 +1,74 @@ +/* + * This file is part of VoxelSniper, licensed under the MIT License (MIT). + * + * Copyright (c) The VoxelBox + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.thevoxelbox.voxelsniper.util; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.trait.BlockTrait; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.text.Text; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class BlockTraitHelper { + /* Attempts to parse all block traits in rawKeyValues. If a failure occurs, + * sends an error message to src and returns empty. Otherwise, returns a + * mapping from the BlockTraits to the parsed values. + */ + public static Optional, Object>> parseKeyValues(Collection rawKeyValues, + BlockState target, + CommandSource src) { + assert rawKeyValues != null; + Map, Object> traitMap = new HashMap, Object>(); + for (String rawKeyValue : rawKeyValues) { + if (rawKeyValue.indexOf('=') == -1) { + src.sendMessage(Text.of("Unable to parse")); + return Optional.empty(); + } + + String[] params = rawKeyValue.split("="); + String key = params[0]; + String value = params[1]; + + Optional> optTrait = target.getTrait(key); + if (!optTrait.isPresent()) { + src.sendMessage(Text.of("Unknown block trait '", key, "'")); + return Optional.empty(); + } + + BlockTrait trait = optTrait.get(); + Optional optValue = trait.parseValue(value); + if (!optValue.isPresent()) { + src.sendMessage(Text.of("Unknown value '", value, "' for key '", key, "'")); + return Optional.empty(); + } + traitMap.put(optTrait.get(), optValue.get()); + } + + return Optional.of(traitMap); + } +} From 98ba668c4be5b4ac3fe44ecd33c4e2ee1ccfd0c4 Mon Sep 17 00:00:00 2001 From: lipian Date: Sat, 19 Oct 2019 20:06:51 -0700 Subject: [PATCH 2/4] Properly merging PerformBrush conflicts --- .../com/thevoxelbox/voxelsniper/brush/PerformBrush.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java index 909279b3..8be410a4 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java @@ -107,11 +107,7 @@ protected boolean perform(SnipeData v, int x, int y, int z) { BlockState current = this.world.getBlock(x, y, z); switch (replaceMethod) { case TYPE: -<<<<<<< HEAD - if (current.getType() != v.getReplaceState().getType()) { -======= if (!sameBlockType(current, v.getReplaceState())) { ->>>>>>> 8e15cbd... Updating the ink performers to work better return false; } break; @@ -121,12 +117,8 @@ protected boolean perform(SnipeData v, int x, int y, int z) { } break; case COMBO: -<<<<<<< HEAD - if (current != v.getReplaceState()) { -======= if (!sameBlockType(current, v.getReplaceState()) || !BlockHelper.hasTraits(current, v.getReplaceInkTraits())) { ->>>>>>> 8e15cbd... Updating the ink performers to work better return false; } break; From 1bf553bbed1446ab3ee17b3409401761d59f01b0 Mon Sep 17 00:00:00 2001 From: lipian Date: Thu, 24 Oct 2019 14:07:01 -0700 Subject: [PATCH 3/4] Fixing problem where ink wouldn't update the state --- src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java b/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java index 1b67cf92..c588e710 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/SnipeData.java @@ -24,6 +24,7 @@ */ package com.thevoxelbox.voxelsniper; +import com.thevoxelbox.voxelsniper.util.BlockHelper; import com.thevoxelbox.voxelsniper.util.VoxelList; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockState; @@ -178,6 +179,7 @@ public Map, Object> getVoxelInkTraits() { public void setVoxelInkTraits(Map, Object> traitValues) { this.voxelInkTraits.clear(); this.voxelInkTraits.putAll(traitValues); + this.voxelState = BlockHelper.addTraits(this.voxelState.getType().getDefaultState(), traitValues); } public Map, Object> getReplaceInkTraits() { @@ -189,6 +191,7 @@ public Map, Object> getReplaceInkTraits() { public void setReplaceInkTraits(Map, Object> traitValues) { this.replaceInkTraits.clear(); this.replaceInkTraits.putAll(traitValues); + this.replaceState = BlockHelper.addTraits(this.replaceState.getType().getDefaultState(), traitValues); } public Sniper owner() { From 5bc65b0402c0110e4bbac51663334b45904765d9 Mon Sep 17 00:00:00 2001 From: lipian Date: Fri, 22 Nov 2019 18:40:09 -0800 Subject: [PATCH 4/4] Revamping the VoxelList so that it actually functions. --- .../com/thevoxelbox/voxelsniper/Message.java | 20 +---- .../voxelsniper/brush/PerformBrush.java | 24 +++++- .../voxelsniper/command/VoxelListCommand.java | 27 +++++-- .../voxelsniper/util/BlockHelper.java | 21 +++-- .../voxelsniper/util/VoxelList.java | 80 ++++++++----------- 5 files changed, 90 insertions(+), 82 deletions(-) diff --git a/src/main/java/com/thevoxelbox/voxelsniper/Message.java b/src/main/java/com/thevoxelbox/voxelsniper/Message.java index 61795836..0ad6a60b 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/Message.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/Message.java @@ -169,24 +169,6 @@ public void voxel() { * Display voxel list. */ public void voxelList() { - if (this.snipeData.getVoxelList().isEmpty()) { - this.snipeData.sendMessage(TextColors.DARK_GREEN, "No blocks selected!"); - } else { - Text.Builder returnValueBuilder = Text.builder(); - returnValueBuilder.append(Text.of(TextColors.DARK_GREEN, "Block Types Selected: ")); - - StringBuilder vl = new StringBuilder(); - for (BlockType type : this.snipeData.getVoxelList().getWildcardTypes()) { - vl.append(type.getId()); - vl.append(" "); - } - for (BlockState type : this.snipeData.getVoxelList().getSpecificTypes()) { - vl.append(type.getId()); - vl.append(" "); - } - returnValueBuilder.append(Text.of(TextColors.AQUA, vl.toString().trim())); - - this.snipeData.sendMessage(returnValueBuilder.toText()); - } + this.snipeData.sendMessage(Text.of(TextColors.DARK_GREEN, this.snipeData.getVoxelList())); } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java index 8be410a4..f419ebfa 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/brush/PerformBrush.java @@ -44,7 +44,7 @@ public abstract class PerformBrush extends Brush { - private static final Pattern PERFORMER = Pattern.compile("([mic])([mic]?)(p?)"); + private static final Pattern PERFORMER = Pattern.compile("([mic])([micnx]?)(p?)"); protected PerformerType placeMethod = PerformerType.TYPE; protected PerformerType replaceMethod = PerformerType.NONE; @@ -79,6 +79,10 @@ private PerformerType stringToMethod(String rawMethod) { return PerformerType.TRAITS; case "c": return PerformerType.COMBO; + case "n": + return PerformerType.LIST_INCLUDE; + case "x": + return PerformerType.LIST_EXCLUDE; } return PerformerType.NONE; } @@ -122,6 +126,16 @@ protected boolean perform(SnipeData v, int x, int y, int z) { return false; } break; + case LIST_INCLUDE: + if (!v.getVoxelList().contains(current)) { + return false; + } + break; + case LIST_EXCLUDE: + if (v.getVoxelList().contains(current)) { + return false; + } + break; case NONE: default: break; @@ -154,6 +168,8 @@ public enum PerformerType { TYPE, TRAITS, COMBO, + LIST_INCLUDE, + LIST_EXCLUDE, NONE; public String toString() { @@ -164,11 +180,15 @@ public String toString() { return "Material"; case COMBO: return "Combo"; + case LIST_INCLUDE: + return "List Include"; + case LIST_EXCLUDE: + return "List Exclude"; case NONE: return "None"; } - return "Unkown"; + return "Unknown"; } } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java index c1f012ea..a74aa4c3 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/command/VoxelListCommand.java @@ -44,30 +44,39 @@ import org.spongepowered.api.text.format.TextColors; import java.util.Optional; +import java.util.regex.PatternSyntaxException; public class VoxelListCommand implements CommandExecutor { public static void setup(Object plugin) { Sponge.getCommandManager().register(plugin, CommandSpec.builder() - .arguments(GenericArguments.playerOrSource(Text.of("sniper")), + .arguments( + GenericArguments.playerOrSource(Text.of("sniper")), GenericArguments.optional(GenericArguments.remainingJoinedStrings(Text.of("args")))) - .executor(new VoxelListCommand()).permission(VoxelSniperConfiguration.PERMISSION_SNIPER) + .executor(new VoxelListCommand()) + .permission(VoxelSniperConfiguration.PERMISSION_SNIPER) .description(Text.of("VoxelSniper material list selection")).build(), "vl"); } @Override - public CommandResult execute(CommandSource src, CommandContext gargs) throws CommandException { + public CommandResult execute(CommandSource src, CommandContext gargs) { Player player = (Player) gargs.getOne("sniper").get(); Sniper sniper = SniperManager.get().getSniperForPlayer(player); Optional oargs = gargs.getOne("args"); SnipeData snipeData = sniper.getSnipeData(sniper.getCurrentToolId()); + if (!oargs.isPresent()) { - snipeData.getVoxelList().add(BlockHelper.stateOrWhereLooking(Optional.empty(), player).get()); + Optional optBlock = BlockHelper.stateOrWhereLooking(Optional.empty(), player); + if (optBlock.isPresent()) { + snipeData.getVoxelList().add(optBlock.get()); + } + snipeData.getVoxelMessage().voxelList(); return CommandResult.success(); } + String[] args = oargs.get().split(" "); if (args[0].equalsIgnoreCase("clear")) { snipeData.getVoxelList().clear(); @@ -76,10 +85,12 @@ public CommandResult execute(CommandSource src, CommandContext gargs) throws Com } for (String arg : args) { - boolean remove = arg.startsWith("-"); - if (remove) { + boolean remove = false; + if (arg.startsWith("-")) { arg = arg.substring(1); + remove = true; } + Optional type = Sponge.getRegistry().getType(BlockType.class, arg); if (type.isPresent()) { if (remove) { @@ -87,6 +98,7 @@ public CommandResult execute(CommandSource src, CommandContext gargs) throws Com } else { snipeData.getVoxelList().add(type.get()); } + } else { Optional state = Sponge.getRegistry().getType(BlockState.class, arg); if (state.isPresent()) { @@ -96,10 +108,11 @@ public CommandResult execute(CommandSource src, CommandContext gargs) throws Com snipeData.getVoxelList().add(state.get()); } } else { - player.sendMessage(Text.of(TextColors.RED, "Material not found.")); + player.sendMessage(Text.of(TextColors.RED, "Material '", arg, "' not found.")); } } } + snipeData.getVoxelMessage().voxelList(); return CommandResult.success(); } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java index bb71a1fc..72c57e84 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/util/BlockHelper.java @@ -32,7 +32,7 @@ import org.spongepowered.api.data.property.block.SolidCubeProperty; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.util.blockray.BlockRay; -import org.spongepowered.api.world.Location; +import org.spongepowered.api.util.blockray.BlockRayHit; import org.spongepowered.api.world.World; import java.util.Map; @@ -98,14 +98,19 @@ public static Optional stateOrWhereLooking(Optional rawState return Sponge.getRegistry().getType(BlockState.class, rawState.get()); } - Location targetBlock = null; - BlockRay.BlockRayBuilder rayBuilder = - BlockRay.from(player).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)); - BlockRay ray = rayBuilder.build(); - while (ray.hasNext()) { - targetBlock = ray.next().getLocation(); + BlockRay ray = + BlockRay.from(player) + .stopFilter( + BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1) + ).build(); + + Optional> optRayHit = ray.end(); + if (!optRayHit.isPresent()) { + return Optional.empty(); } - return Optional.of(targetBlock.getBlock()); + BlockRayHit rayHit = optRayHit.get(); + BlockState block = rayHit.getExtent().getBlock(rayHit.getBlockPosition()); + return Optional.of(block); } } diff --git a/src/main/java/com/thevoxelbox/voxelsniper/util/VoxelList.java b/src/main/java/com/thevoxelbox/voxelsniper/util/VoxelList.java index 1af5759a..6885c000 100644 --- a/src/main/java/com/thevoxelbox/voxelsniper/util/VoxelList.java +++ b/src/main/java/com/thevoxelbox/voxelsniper/util/VoxelList.java @@ -28,7 +28,6 @@ import org.spongepowered.api.block.BlockState; import org.spongepowered.api.block.BlockType; -import java.util.Iterator; import java.util.Set; /** @@ -46,77 +45,66 @@ public class VoxelList { * @param i */ public void add(BlockState i) { - if (this.wildcardTypes.contains(i.getType())) { - return; + if (!this.wildcardTypes.contains(i.getType())) { + this.specificTypes.add(i); } - this.specificTypes.add(i); } public void add(BlockType t) { - if (this.wildcardTypes.contains(t)) { - return; + if (!this.wildcardTypes.contains(t)) { + removeStatesOfType(t); + this.wildcardTypes.add(t); } - for (Iterator it = this.specificTypes.iterator(); it.hasNext();) { - BlockState state = it.next(); - if (state.getType() == t) { - it.remove(); - } - } - this.wildcardTypes.add(t); } - public boolean remove(BlockState state) { - return this.specificTypes.remove(state); + public void remove(BlockState state) { + this.specificTypes.remove(state); } - public boolean remove(BlockType t) { - boolean removed = this.wildcardTypes.remove(t); - for (Iterator it = this.specificTypes.iterator(); it.hasNext();) { - BlockState state = it.next(); - if (state.getType() == t) { - it.remove(); - removed = true; - } + public void remove(BlockType t) { + if(!this.wildcardTypes.remove(t)) { + removeStatesOfType(t); } - return removed; } public boolean contains(BlockState state) { - return this.specificTypes.contains(state); + return this.specificTypes.contains(state) || + this.wildcardTypes.contains(state.getType()); } public boolean contains(BlockType type) { return this.wildcardTypes.contains(type); } - public boolean containsAny(BlockType type) { - boolean contains = this.wildcardTypes.contains(type); - if (!contains) { - for (Iterator it = this.specificTypes.iterator(); it.hasNext();) { - BlockState state = it.next(); - if (state.getType() == type) { - return true; - } - } - } - return contains; - } - public void clear() { this.specificTypes.clear(); this.wildcardTypes.clear(); } - public boolean isEmpty() { - return this.specificTypes.isEmpty() && this.wildcardTypes.isEmpty(); - } + @Override + public String toString() { + if (this.specificTypes.isEmpty() && this.wildcardTypes.isEmpty()) { + return "No blocks selected!"; + } - public Set getWildcardTypes() { - return this.wildcardTypes; - } + StringBuilder sb = new StringBuilder("Selected Block types: \n"); + + for (BlockType type : wildcardTypes) { + sb.append(" ") + .append(type.getId()) + .append("\n"); + } - public Set getSpecificTypes() { - return this.specificTypes; + for (BlockState state : specificTypes) { + sb.append(" ") + .append(state.getId()) + .append("\n"); + } + + return sb.toString().trim(); } + private void removeStatesOfType(BlockType type) { + this.specificTypes.removeIf((other) -> other.getType().equals(type)); + } }