diff --git a/paper-api/src/main/java/io/papermc/paper/math/BlockPosition.java b/paper-api/src/main/java/io/papermc/paper/math/BlockPosition.java new file mode 100644 index 0000000000..c358bfdefc --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/BlockPosition.java @@ -0,0 +1,100 @@ +package io.papermc.paper.math; + +import org.bukkit.Axis; +import org.bukkit.block.BlockFace; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * A position represented with integers. + *
+ * May see breaking changes until Experimental annotation is removed. + * + * @see FinePosition + */ +@ApiStatus.Experimental +@NullMarked +public interface BlockPosition extends Position { + + @Override + default double x() { + return this.blockX(); + } + + @Override + default double y() { + return this.blockY(); + } + + @Override + default double z() { + return this.blockZ(); + } + + @Override + default boolean isBlock() { + return true; + } + + @Override + default boolean isFine() { + return false; + } + + @Override + default BlockPosition toBlock() { + return this; + } + + @Override + default BlockPosition offset(final int x, final int y, final int z) { + return x == 0 && y == 0 && z == 0 ? this : new BlockPositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z); + } + + @Override + default FinePosition offset(final double x, final double y, final double z) { + return new FinePositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z); + } + + /** + * Returns a block position offset by 1 in the direction specified. + * + * @param blockFace the block face to offset towards + * @return the offset block position + */ + @Contract(value = "_ -> new", pure = true) + default BlockPosition offset(final BlockFace blockFace) { + return this.offset(blockFace, 1); + } + + /** + * Returns a block position offset in the direction specified + * multiplied by the amount. + * + * @param blockFace the block face to offset towards + * @param amount the number of times to move in that direction + * @return the offset block position + */ + @Contract(pure = true) + default BlockPosition offset(final BlockFace blockFace, final int amount) { + return amount == 0 ? this : new BlockPositionImpl(this.blockX() + (blockFace.getModX() * amount), this.blockY() + (blockFace.getModY() * amount), this.blockZ() + (blockFace.getModZ() * amount)); + } + + /** + * Returns a block position offset by the amount along + * the specified axis. + * + * @param axis the axis to offset along + * @param amount the amount to offset along that axis + * @return the offset block position + */ + @Contract(pure = true) + default BlockPosition offset(final Axis axis, final int amount) { + return amount == 0 ? this : switch (axis) { + case X -> new BlockPositionImpl(this.blockX() + amount, this.blockY(), this.blockZ()); + case Y -> new BlockPositionImpl(this.blockX(), this.blockY() + amount, this.blockZ()); + case Z -> new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ() + amount); + }; + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/BlockPositionImpl.java b/paper-api/src/main/java/io/papermc/paper/math/BlockPositionImpl.java new file mode 100644 index 0000000000..eb5a3f26c7 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/BlockPositionImpl.java @@ -0,0 +1,4 @@ +package io.papermc.paper.math; + +record BlockPositionImpl(int blockX, int blockY, int blockZ) implements BlockPosition { +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/FinePosition.java b/paper-api/src/main/java/io/papermc/paper/math/FinePosition.java new file mode 100644 index 0000000000..b9c0065d8a --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/FinePosition.java @@ -0,0 +1,57 @@ +package io.papermc.paper.math; + +import org.bukkit.util.NumberConversions; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +/** + * A position represented with doubles. + *
+ * May see breaking changes until Experimental annotation is removed. + * + * @see BlockPosition + */ +@ApiStatus.Experimental +@NullMarked +public interface FinePosition extends Position { + + @Override + default int blockX() { + return NumberConversions.floor(this.x()); + } + + @Override + default int blockY() { + return NumberConversions.floor(this.y()); + } + + @Override + default int blockZ() { + return NumberConversions.floor(this.z()); + } + + @Override + default boolean isBlock() { + return false; + } + + @Override + default boolean isFine() { + return true; + } + + @Override + default BlockPosition toBlock() { + return new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ()); + } + + @Override + default FinePosition offset(final int x, final int y, final int z) { + return this.offset((double) x, y, z); + } + + @Override + default FinePosition offset(final double x, final double y, final double z) { + return x == 0.0 && y == 0.0 && z == 0.0 ? this : new FinePositionImpl(this.x() + x, this.y() + y, this.z() + z); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/FinePositionImpl.java b/paper-api/src/main/java/io/papermc/paper/math/FinePositionImpl.java new file mode 100644 index 0000000000..93476aaf8d --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/FinePositionImpl.java @@ -0,0 +1,4 @@ +package io.papermc.paper.math; + +record FinePositionImpl(double x, double y, double z) implements FinePosition { +} diff --git a/paper-api/src/main/java/io/papermc/paper/math/Position.java b/paper-api/src/main/java/io/papermc/paper/math/Position.java new file mode 100644 index 0000000000..0e6a6a6738 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/math/Position.java @@ -0,0 +1,192 @@ +package io.papermc.paper.math; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NullMarked; + +/** + * Common interface for {@link FinePosition} and {@link BlockPosition}. + *
+ * May see breaking changes until Experimental annotation is removed.
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface Position {
+
+ FinePosition FINE_ZERO = new FinePositionImpl(0, 0, 0);
+ BlockPosition BLOCK_ZERO = new BlockPositionImpl(0, 0, 0);
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockX();
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockY();
+
+ /**
+ * Gets the block x value for this position
+ *
+ * @return the block x value
+ */
+ int blockZ();
+
+ /**
+ * Gets the x value for this position
+ *
+ * @return the x value
+ */
+ double x();
+
+ /**
+ * Gets the y value for this position
+ *
+ * @return the y value
+ */
+ double y();
+
+ /**
+ * Gets the z value for this position
+ *
+ * @return the z value
+ */
+ double z();
+
+ /**
+ * Checks of this position represents a {@link BlockPosition}
+ *
+ * @return true if block
+ */
+ boolean isBlock();
+
+ /**
+ * Checks if this position represents a {@link FinePosition}
+ *
+ * @return true if fine
+ */
+ boolean isFine();
+
+ /**
+ * Checks if each component of this position is finite.
+ */
+ default boolean isFinite() {
+ return Double.isFinite(this.x()) && Double.isFinite(this.y()) && Double.isFinite(this.z());
+ }
+
+ /**
+ * Returns a position offset by the specified amounts.
+ *
+ * @param x x value to offset
+ * @param y y value to offset
+ * @param z z value to offset
+ * @return the offset position
+ */
+ Position offset(int x, int y, int z);
+
+ /**
+ * Returns a position offset by the specified amounts.
+ *
+ * @param x x value to offset
+ * @param y y value to offset
+ * @param z z value to offset
+ * @return the offset position
+ */
+ FinePosition offset(double x, double y, double z);
+
+ /**
+ * Returns a new position at the center of the block position this represents
+ *
+ * @return a new center position
+ */
+ @Contract(value = "-> new", pure = true)
+ default FinePosition toCenter() {
+ return new FinePositionImpl(this.blockX() + 0.5, this.blockY() + 0.5, this.blockZ() + 0.5);
+ }
+
+ /**
+ * Returns the block position of this position
+ * or itself if it already is a block position
+ *
+ * @return the block position
+ */
+ @Contract(pure = true)
+ BlockPosition toBlock();
+
+ /**
+ * Converts this position to a vector
+ *
+ * @return a new vector
+ */
+ @Contract(value = "-> new", pure = true)
+ default Vector toVector() {
+ return new Vector(this.x(), this.y(), this.z());
+ }
+
+ /**
+ * Creates a new location object at this position with the specified world
+ *
+ * @param world the world for the location object
+ * @return a new location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ default Location toLocation(final World world) {
+ return new Location(world, this.x(), this.y(), this.z());
+ }
+
+ /**
+ * Creates a position at the coordinates
+ *
+ * @param x x coord
+ * @param y y coord
+ * @param z z coord
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static BlockPosition block(final int x, final int y, final int z) {
+ return new BlockPositionImpl(x, y, z);
+ }
+
+ /**
+ * Creates a position from the location.
+ *
+ * @param location the location to copy the position of
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static BlockPosition block(final Location location) {
+ return new BlockPositionImpl(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+ }
+
+ /**
+ * Creates a position at the coordinates
+ *
+ * @param x x coord
+ * @param y y coord
+ * @param z z coord
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static FinePosition fine(final double x, final double y, final double z) {
+ return new FinePositionImpl(x, y, z);
+ }
+
+ /**
+ * Creates a position from the location.
+ *
+ * @param location the location to copy the position of
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static FinePosition fine(final Location location) {
+ return new FinePositionImpl(location.getX(), location.getY(), location.getZ());
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/Location.java b/paper-api/src/main/java/org/bukkit/Location.java
index 734054f1e8..bc8a64d54e 100644
--- a/paper-api/src/main/java/org/bukkit/Location.java
+++ b/paper-api/src/main/java/org/bukkit/Location.java
@@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable;
* magnitude than 360 are valid, but may be normalized to any other equivalent
* representation by the implementation.
*/
-public class Location implements Cloneable, ConfigurationSerializable {
+public class Location implements Cloneable, ConfigurationSerializable, io.papermc.paper.math.FinePosition { // Paper
private Reference