diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java deleted file mode 100644 index 2ab9471e0..000000000 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelative.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.minestom.server.command.builder.arguments.relative; - -import net.minestom.server.command.builder.arguments.Argument; -import org.jetbrains.annotations.NotNull; - -/** - * Common interface for all the relative location arguments. - * - * @param the relative location type - */ -public abstract class ArgumentRelative extends Argument { - - public static final String RELATIVE_CHAR = "~"; - - public static final int INVALID_NUMBER_COUNT_ERROR = 1; - public static final int INVALID_NUMBER_ERROR = 2; - - private final int numberCount; - - public ArgumentRelative(@NotNull String id, int numberCount) { - super(id, true); - this.numberCount = numberCount; - } - - /** - * Gets the amount of numbers that this relative location needs. - * - * @return the amount of coordinate required - */ - public int getNumberCount() { - return numberCount; - } -} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java index f7c5e90d5..403cad931 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeBlockPosition.java @@ -8,77 +8,19 @@ import net.minestom.server.utils.StringUtils; import net.minestom.server.utils.location.RelativeVec; import org.jetbrains.annotations.NotNull; +import java.util.function.Function; + /** * Represents a block position with 3 integers (x;y;z) which can take relative coordinates. *

* Example: 5 ~ -3 */ -public class ArgumentRelativeBlockPosition extends ArgumentRelative { +public class ArgumentRelativeBlockPosition extends ArgumentRelativeVec { public ArgumentRelativeBlockPosition(@NotNull String id) { super(id, 3); } - @NotNull - @Override - public RelativeVec parse(@NotNull String input) throws ArgumentSyntaxException { - final String[] split = input.split(StringUtils.SPACE); - // Check if the value has enough element to be correct - if (split.length != getNumberCount()) { - throw new ArgumentSyntaxException("Invalid number of values", input, INVALID_NUMBER_COUNT_ERROR); - } - - int x = 0, y = 0, z = 0; - boolean relativeX = false; - boolean relativeY = false; - boolean relativeZ = false; - for (int i = 0; i < split.length; i++) { - final String element = split[i]; - if (element.startsWith(RELATIVE_CHAR)) { - - if (i == 0) { - relativeX = true; - } else if (i == 1) { - relativeY = true; - } else if (i == 2) { - relativeZ = true; - } - - if (element.length() != RELATIVE_CHAR.length()) { - try { - final String potentialNumber = element.substring(1); - final int number = Integer.parseInt(potentialNumber); - if (i == 0) { - x = number; - } else if (i == 1) { - y = number; - } else if (i == 2) { - z = number; - } - } catch (NumberFormatException e) { - throw new ArgumentSyntaxException("Invalid number", input, INVALID_NUMBER_ERROR); - } - } - - } else { - try { - final int number = Integer.parseInt(element); - if (i == 0) { - x = number; - } else if (i == 1) { - y = number; - } else if (i == 2) { - z = number; - } - } catch (NumberFormatException e) { - throw new ArgumentSyntaxException("Invalid number", input, INVALID_NUMBER_ERROR); - } - } - } - - return new RelativeVec(new Vec(x, y, z), relativeX, relativeY, relativeZ); - } - @Override public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); @@ -91,4 +33,14 @@ public class ArgumentRelativeBlockPosition extends ArgumentRelative public String toString() { return String.format("RelativeBlockPosition<%s>", getId()); } + + @Override + Function getRelativeNumberParser() { + return Double::parseDouble; + } + + @Override + Function getAbsoluteNumberParser() { + return Integer::parseInt; + } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java new file mode 100644 index 000000000..f516e49e5 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec.java @@ -0,0 +1,94 @@ +package net.minestom.server.command.builder.arguments.relative; + +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.utils.StringUtils; +import net.minestom.server.utils.location.RelativeVec; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; +import java.util.function.Function; + +import static net.minestom.server.utils.location.RelativeVec.CoordinateType.*; + +/** + * Common interface for all the relative location arguments. + */ +abstract class ArgumentRelativeVec extends Argument { + + private static final char RELATIVE_CHAR = '~'; + private static final char LOCAL_CHAR = '^'; + private static final Set MODIFIER_CHARS = Set.of(RELATIVE_CHAR, LOCAL_CHAR); + + public static final int INVALID_NUMBER_COUNT_ERROR = 1; + public static final int INVALID_NUMBER_ERROR = 2; + public static final int MIXED_TYPE_ERROR = 3; + + private final int numberCount; + + public ArgumentRelativeVec(@NotNull String id, int numberCount) { + super(id, true); + this.numberCount = numberCount; + } + + abstract Function getRelativeNumberParser(); + + abstract Function getAbsoluteNumberParser(); + + @NotNull + @Override + public RelativeVec parse(@NotNull String input) throws ArgumentSyntaxException { + final String[] split = input.split(StringUtils.SPACE); + if (split.length != getNumberCount()) { + throw new ArgumentSyntaxException("Invalid number of values", input, INVALID_NUMBER_COUNT_ERROR); + } + + double[] coordinates = new double[split.length]; + boolean[] isRelative = new boolean[split.length]; + RelativeVec.CoordinateType type = null; + for (int i = 0; i < split.length; i++) { + final String element = split[i]; + try { + final char modifierChar = element.charAt(0); + if (MODIFIER_CHARS.contains(modifierChar)) { + isRelative[i] = true; + + if (type == null) { + type = modifierChar == LOCAL_CHAR ? LOCAL : RELATIVE; + } else if (type != (modifierChar == LOCAL_CHAR ? LOCAL : RELATIVE)) { + throw new ArgumentSyntaxException("Cannot mix world & local coordinates (everything must either use ^ or not)", input, MIXED_TYPE_ERROR); + } + + if (element.length() > 1) { + final String potentialNumber = element.substring(1); + coordinates[i] = getRelativeNumberParser().apply(potentialNumber).doubleValue(); + } + } else { + if (type == null) { + type = ABSOLUTE; + } else if (type == LOCAL) { + throw new ArgumentSyntaxException("Cannot mix world & local coordinates (everything must either use ^ or not)", input, MIXED_TYPE_ERROR); + } + coordinates[i] = getAbsoluteNumberParser().apply(element).doubleValue(); + } + } catch (NumberFormatException e) { + throw new ArgumentSyntaxException("Invalid number", input, INVALID_NUMBER_ERROR); + } + } + + return new RelativeVec(split.length == 3 ? + new Vec(coordinates[0], coordinates[1], coordinates[2]) : new Vec(coordinates[0], coordinates[1]), + type, + isRelative[0], split.length == 3 && isRelative[1], isRelative[split.length == 3 ? 2 : 1]); + } + + /** + * Gets the amount of numbers that this relative location needs. + * + * @return the amount of coordinate required + */ + public int getNumberCount() { + return numberCount; + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java index 112f53773..6e71d42b1 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec2.java @@ -1,73 +1,23 @@ package net.minestom.server.command.builder.arguments.relative; import net.minestom.server.command.builder.NodeMaker; -import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.coordinate.Vec; import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; -import net.minestom.server.utils.StringUtils; -import net.minestom.server.utils.location.RelativeVec; import org.jetbrains.annotations.NotNull; +import java.util.function.Function; + /** * Represents a {@link Vec} with 2 floating numbers (x;z) which can take relative coordinates. *

* Example: -1.2 ~ */ -public class ArgumentRelativeVec2 extends ArgumentRelative { +public class ArgumentRelativeVec2 extends ArgumentRelativeVec { public ArgumentRelativeVec2(@NotNull String id) { super(id, 2); } - @NotNull - @Override - public RelativeVec parse(@NotNull String input) throws ArgumentSyntaxException { - final String[] split = input.split(StringUtils.SPACE); - // Check if the value has enough element to be correct - if (split.length != getNumberCount()) { - throw new ArgumentSyntaxException("Invalid number of values", input, INVALID_NUMBER_COUNT_ERROR); - } - - double x = 0, z = 0; - boolean relativeX = false; - boolean relativeZ = false; - - for (int i = 0; i < split.length; i++) { - final String element = split[i]; - try { - if (element.startsWith(RELATIVE_CHAR)) { - if (i == 0) { - relativeX = true; - } else if (i == 1) { - relativeZ = true; - } - - if (element.length() != RELATIVE_CHAR.length()) { - final String potentialNumber = element.substring(1); - final float number = Float.parseFloat(potentialNumber); - if (i == 0) { - x = number; - } else if (i == 1) { - z = number; - } - } - - } else { - final float number = Float.parseFloat(element); - if (i == 0) { - x = number; - } else if (i == 1) { - z = number; - } - } - } catch (NumberFormatException e) { - throw new ArgumentSyntaxException("Invalid number", input, INVALID_NUMBER_ERROR); - } - } - - return new RelativeVec(new Vec(x, z), relativeX, false, relativeZ); - } - @Override public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); @@ -80,4 +30,14 @@ public class ArgumentRelativeVec2 extends ArgumentRelative { public String toString() { return String.format("RelativeVec2<%s>", getId()); } -} \ No newline at end of file + + @Override + Function getRelativeNumberParser() { + return Double::parseDouble; + } + + @Override + Function getAbsoluteNumberParser() { + return Double::parseDouble; + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java index 55c00eac8..2616203ec 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/relative/ArgumentRelativeVec3.java @@ -1,79 +1,23 @@ package net.minestom.server.command.builder.arguments.relative; import net.minestom.server.command.builder.NodeMaker; -import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.coordinate.Vec; import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; -import net.minestom.server.utils.StringUtils; -import net.minestom.server.utils.location.RelativeVec; import org.jetbrains.annotations.NotNull; +import java.util.function.Function; + /** * Represents a {@link Vec} with 3 floating numbers (x;y;z) which can take relative coordinates. *

* Example: -1.2 ~ 5 */ -public class ArgumentRelativeVec3 extends ArgumentRelative { +public class ArgumentRelativeVec3 extends ArgumentRelativeVec { public ArgumentRelativeVec3(@NotNull String id) { super(id, 3); } - @NotNull - @Override - public RelativeVec parse(@NotNull String input) throws ArgumentSyntaxException { - final String[] split = input.split(StringUtils.SPACE); - // Check if the value has enough element to be correct - if (split.length != getNumberCount()) { - throw new ArgumentSyntaxException("Invalid number of values", input, INVALID_NUMBER_COUNT_ERROR); - } - - double x = 0, y = 0, z = 0; - boolean relativeX = false; - boolean relativeY = false; - boolean relativeZ = false; - for (int i = 0; i < split.length; i++) { - final String element = split[i]; - try { - if (element.startsWith(RELATIVE_CHAR)) { - if (i == 0) { - relativeX = true; - } else if (i == 1) { - relativeY = true; - } else if (i == 2) { - relativeZ = true; - } - - if (element.length() != RELATIVE_CHAR.length()) { - final String potentialNumber = element.substring(1); - final float number = Float.parseFloat(potentialNumber); - if (i == 0) { - x = number; - } else if (i == 1) { - y = number; - } else if (i == 2) { - z = number; - } - } - - } else { - final float number = Float.parseFloat(element); - if (i == 0) { - x = number; - } else if (i == 1) { - y = number; - } else if (i == 2) { - z = number; - } - } - } catch (NumberFormatException e) { - throw new ArgumentSyntaxException("Invalid number", input, INVALID_NUMBER_ERROR); - } - } - - return new RelativeVec(new Vec(x, y, z), relativeX, relativeY, relativeZ); - } - @Override public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); @@ -86,4 +30,14 @@ public class ArgumentRelativeVec3 extends ArgumentRelative { public String toString() { return String.format("RelativeVec3<%s>", getId()); } + + @Override + Function getRelativeNumberParser() { + return Double::parseDouble; + } + + @Override + Function getAbsoluteNumberParser() { + return Double::parseDouble; + } } diff --git a/src/main/java/net/minestom/server/utils/location/RelativeVec.java b/src/main/java/net/minestom/server/utils/location/RelativeVec.java index 360a1b577..b95e880de 100644 --- a/src/main/java/net/minestom/server/utils/location/RelativeVec.java +++ b/src/main/java/net/minestom/server/utils/location/RelativeVec.java @@ -1,7 +1,6 @@ package net.minestom.server.utils.location; import net.minestom.server.command.CommandSender; -import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.Entity; @@ -18,10 +17,12 @@ import java.util.Objects; public final class RelativeVec { private final Vec vec; + private final CoordinateType coordinateType; private final boolean relativeX, relativeY, relativeZ; - public RelativeVec(@NotNull Vec vec, boolean relativeX, boolean relativeY, boolean relativeZ) { + public RelativeVec(@NotNull Vec vec, @NotNull CoordinateType coordinateType, boolean relativeX, boolean relativeY, boolean relativeZ) { this.vec = vec; + this.coordinateType = coordinateType; this.relativeX = relativeX; this.relativeY = relativeY; this.relativeZ = relativeZ; @@ -30,18 +31,12 @@ public final class RelativeVec { /** * Gets the location based on the relative fields and {@code position}. * - * @param point the relative position + * @param origin the origin position, null if none * @return the location */ - public @NotNull Vec from(@Nullable Point point) { - if (!relativeX && !relativeY && !relativeZ) { - return vec; - } - final var absolute = Objects.requireNonNullElse(point, Vec.ZERO); - final double x = vec.x() + (relativeX ? absolute.x() : 0); - final double y = vec.y() + (relativeY ? absolute.y() : 0); - final double z = vec.z() + (relativeZ ? absolute.z() : 0); - return new Vec(x, y, z); + public @NotNull Vec from(@Nullable Pos origin) { + origin = Objects.requireNonNullElse(origin, Pos.ZERO); + return coordinateType.convert(vec, origin, relativeX, relativeY, relativeZ); } @ApiStatus.Experimental @@ -62,8 +57,11 @@ public final class RelativeVec { * @return the location */ public @NotNull Vec from(@Nullable Entity entity) { - final var entityPosition = entity != null ? entity.getPosition() : Pos.ZERO; - return from(entityPosition); + if (entity != null) { + return from(entity.getPosition().add(0, entity.getEyeHeight(), 0)); + } else { + return from(Pos.ZERO); + } } public @NotNull Vec fromSender(@Nullable CommandSender sender) { @@ -103,4 +101,47 @@ public final class RelativeVec { public boolean isRelativeZ() { return relativeZ; } + + public enum CoordinateType { + RELATIVE((relative, origin, relativeX, relativeY, relativeZ) -> { + if (!relativeX && !relativeY && !relativeZ) { + return relative; + } + final var absolute = Objects.requireNonNullElse(origin, Vec.ZERO); + final double x = relative.x() + (relativeX ? absolute.x() : 0); + final double y = relative.y() + (relativeY ? absolute.y() : 0); + final double z = relative.z() + (relativeZ ? absolute.z() : 0); + return new Vec(x, y, z); + }), + LOCAL((local, origin, relativeX, relativeY, relativeZ) -> { + double double5 = Math.cos(Math.toRadians(origin.yaw() + 90.0f)); + double double6 = Math.sin(Math.toRadians(origin.yaw() + 90.0f)); + double double7 = Math.cos(Math.toRadians(-origin.pitch())); + double double8 = Math.sin(Math.toRadians(-origin.pitch())); + double double9 = Math.cos(Math.toRadians(-origin.pitch() + 90.0f)); + double double10 = Math.sin(Math.toRadians(-origin.pitch() + 90.0f)); + Vec dna11 = new Vec(double5 * double7, double8, double6 * double7); + Vec dna12 = new Vec(double5 * double9, double10, double6 * double9); + Vec dna13 = dna11.cross(dna12).mul(-1); + double double14 = dna11.x() * local.z() + dna12.x() * local.y() + dna13.x() * local.x(); + double double16 = dna11.y() * local.z() + dna12.y() * local.y() + dna13.y() * local.x(); + double double18 = dna11.z() * local.z() + dna12.z() * local.y() + dna13.z() * local.x(); + return new Vec(double14 + origin.x(),double16 + origin.y(),double18 + origin.z()); + }), + ABSOLUTE(((vec, origin, relativeX1, relativeY1, relativeZ1) -> vec)); + + private final CoordinateConverter converter; + + CoordinateType(CoordinateConverter converter) { + this.converter = converter; + } + + private @NotNull Vec convert(Vec vec, Pos origin, boolean relativeX, boolean relativeY, boolean relativeZ) { + return converter.convert(vec, origin, relativeX, relativeY, relativeZ); + } + } + + private interface CoordinateConverter { + @NotNull Vec convert(Vec vec, Pos origin, boolean relativeX, boolean relativeY, boolean relativeZ); + } } diff --git a/src/test/java/demo/commands/SetBlockCommand.java b/src/test/java/demo/commands/SetBlockCommand.java index 7a41da93f..c1b8e226f 100644 --- a/src/test/java/demo/commands/SetBlockCommand.java +++ b/src/test/java/demo/commands/SetBlockCommand.java @@ -1,22 +1,23 @@ package demo.commands; import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.arguments.minecraft.ArgumentBlockState; +import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBlockPosition; import net.minestom.server.entity.Player; -import net.minestom.server.instance.block.Block; -import net.minestom.server.utils.location.RelativeVec; import static net.minestom.server.command.builder.arguments.ArgumentType.BlockState; -import static net.minestom.server.command.builder.arguments.ArgumentType.RelativeVec3; +import static net.minestom.server.command.builder.arguments.ArgumentType.RelativeBlockPosition; public class SetBlockCommand extends Command { public SetBlockCommand() { super("setblock"); + final ArgumentRelativeBlockPosition position = RelativeBlockPosition("position"); + final ArgumentBlockState block = BlockState("block"); + addSyntax((sender, context) -> { - RelativeVec relativeVec = context.get("position"); - Block block = context.get("block"); final Player player = sender.asPlayer(); - player.getInstance().setBlock(relativeVec.from(player), block); - }, RelativeVec3("position"), BlockState("block")); + player.getInstance().setBlock(context.get(position).from(player), context.get(block)); + }, position, block); } } diff --git a/src/test/java/demo/commands/TeleportCommand.java b/src/test/java/demo/commands/TeleportCommand.java index f70313a36..49c9572aa 100644 --- a/src/test/java/demo/commands/TeleportCommand.java +++ b/src/test/java/demo/commands/TeleportCommand.java @@ -39,8 +39,8 @@ public class TeleportCommand extends Command { final Player player = sender.asPlayer(); final RelativeVec relativeVec = context.get("pos"); - final Vec position = relativeVec.from(player); - player.teleport(new Pos(position)); + final Pos position = player.getPosition().withCoord(relativeVec.from(player)); + player.teleport(position); player.sendMessage(Component.text("You have been teleported to " + position)); } }