diff --git a/patches/api/Add-Heightmap-API.patch b/patches/api/Add-Heightmap-API.patch
index ec46462128..a7420f1b86 100644
--- a/patches/api/Add-Heightmap-API.patch
+++ b/patches/api/Add-Heightmap-API.patch
@@ -54,7 +54,7 @@ diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/L
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/Location.java
 +++ b/src/main/java/org/bukkit/Location.java
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
          return centerLoc;
      }
  
diff --git a/patches/api/Add-Position.patch b/patches/api/Add-Position.patch
new file mode 100644
index 0000000000..4696c1e5a4
--- /dev/null
+++ b/patches/api/Add-Position.patch
@@ -0,0 +1,422 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <jake.m.potrebic@gmail.com>
+Date: Sun, 20 Mar 2022 10:42:28 -0700
+Subject: [PATCH] Add Position
+
+
+diff --git a/src/main/java/io/papermc/paper/math/BlockPosition.java b/src/main/java/io/papermc/paper/math/BlockPosition.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/math/BlockPosition.java
+@@ -0,0 +0,0 @@
++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.jetbrains.annotations.NotNull;
++
++/**
++ * A position represented with integers.
++ * <p>
++ * <b>May see breaking changes until Experimental annotation is removed.</b>
++ * @see FinePosition
++ */
++@ApiStatus.Experimental
++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 @NotNull BlockPosition toBlock() {
++        return this;
++    }
++
++    @Override
++    default @NotNull BlockPosition offset(int x, int y, int z) {
++        return x == 0 && y == 0 && z == 0 ? this : new BlockPositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
++    }
++
++    @Override
++    default @NotNull FinePosition offset(double x, double y, double z) {
++        return new FinePositionImpl(this.blockX() + z, 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 @NotNull BlockPosition offset(@NotNull 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 @NotNull BlockPosition offset(@NotNull BlockFace blockFace, 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 @NotNull BlockPosition offset(@NotNull Axis axis, 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/src/main/java/io/papermc/paper/math/BlockPositionImpl.java b/src/main/java/io/papermc/paper/math/BlockPositionImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/math/BlockPositionImpl.java
+@@ -0,0 +0,0 @@
++package io.papermc.paper.math;
++
++record BlockPositionImpl(int blockX, int blockY, int blockZ) implements BlockPosition {
++}
+diff --git a/src/main/java/io/papermc/paper/math/FinePosition.java b/src/main/java/io/papermc/paper/math/FinePosition.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/math/FinePosition.java
+@@ -0,0 +0,0 @@
++package io.papermc.paper.math;
++
++import org.bukkit.util.NumberConversions;
++import org.bukkit.util.Vector;
++import org.jetbrains.annotations.ApiStatus;
++import org.jetbrains.annotations.NotNull;
++
++/**
++ * A position represented with doubles.
++ * <p>
++ * <b>May see breaking changes until Experimental annotation is removed.</b>
++ * @see BlockPosition
++ */
++@ApiStatus.Experimental
++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 @NotNull BlockPosition toBlock() {
++        return new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ());
++    }
++
++    @Override
++    default @NotNull FinePosition offset(int x, int y, int z) {
++        return this.offset((double) x, y, z);
++    }
++
++    @Override
++    default @NotNull FinePosition offset(double x, double y, 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/src/main/java/io/papermc/paper/math/FinePositionImpl.java b/src/main/java/io/papermc/paper/math/FinePositionImpl.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/math/FinePositionImpl.java
+@@ -0,0 +0,0 @@
++package io.papermc.paper.math;
++
++record FinePositionImpl(double x, double y, double z) implements FinePosition {
++}
+diff --git a/src/main/java/io/papermc/paper/math/Position.java b/src/main/java/io/papermc/paper/math/Position.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/math/Position.java
+@@ -0,0 +0,0 @@
++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.jetbrains.annotations.NotNull;
++
++/**
++ * Common interface for {@link FinePosition} and {@link BlockPosition}.
++ * <p>
++ * <b>May see breaking changes until Experimental annotation is removed.</b>
++ */
++@ApiStatus.Experimental
++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();
++
++    /**
++     * 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
++     */
++    @NotNull 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
++     */
++    @NotNull 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 @NotNull 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)
++    @NotNull BlockPosition toBlock();
++
++    /**
++     * Converts this position to a vector
++     *
++     * @return a new vector
++     */
++    @Contract(value = "-> new", pure = true)
++    default @NotNull 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 @NotNull Location toLocation(@NotNull 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 @NotNull BlockPosition block(int x, int y, 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 @NotNull BlockPosition block(@NotNull 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 @NotNull FinePosition fine(double x, double y, 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 @NotNull FinePosition fine(@NotNull Location location) {
++        return new FinePositionImpl(location.getX(), location.getY(), location.getZ());
++    }
++}
+diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java
+index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
+--- a/src/main/java/org/bukkit/Location.java
++++ b/src/main/java/org/bukkit/Location.java
+@@ -0,0 +0,0 @@ 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<World> world;
+     private double x;
+     private double y;
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+         }
+         return pitch;
+     }
++
++    // Paper - add Position
++    @Override
++    public double x() {
++        return this.getX();
++    }
++
++    @Override
++    public double y() {
++        return this.getY();
++    }
++
++    @Override
++    public double z() {
++        return this.getZ();
++    }
++
++    @Override
++    public @NotNull Location toLocation(@NotNull World world) {
++        return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch());
++    }
++    // Paper end
+ }
diff --git a/patches/api/Add-getNearbyXXX-methods-to-Location.patch b/patches/api/Add-getNearbyXXX-methods-to-Location.patch
index 6ce1b74cbd..3b22cf5b4b 100644
--- a/patches/api/Add-getNearbyXXX-methods-to-Location.patch
+++ b/patches/api/Add-getNearbyXXX-methods-to-Location.patch
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  /**
   * Represents a 3-dimensional position in a world.
   * <br>
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
          centerLoc.setZ(getBlockZ() + 0.5);
          return centerLoc;
      }
diff --git a/patches/api/Allow-Blocks-to-be-accessed-via-a-long-key.patch b/patches/api/Allow-Blocks-to-be-accessed-via-a-long-key.patch
index efec8e8c18..6b3a1ce13a 100644
--- a/patches/api/Allow-Blocks-to-be-accessed-via-a-long-key.patch
+++ b/patches/api/Allow-Blocks-to-be-accessed-via-a-long-key.patch
@@ -29,7 +29,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  import java.util.function.Predicate;
  import org.bukkit.entity.Entity;
  import org.bukkit.entity.LivingEntity;
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
          blockLoc.setZ(getBlockZ());
          return blockLoc;
      }
diff --git a/patches/api/Expand-Explosions-API.patch b/patches/api/Expand-Explosions-API.patch
index e3df4533f5..9a8c8d1d5d 100644
--- a/patches/api/Expand-Explosions-API.patch
+++ b/patches/api/Expand-Explosions-API.patch
@@ -17,7 +17,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  import org.bukkit.util.NumberConversions;
  import org.bukkit.util.Vector;
  import org.jetbrains.annotations.NotNull;
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
          return centerLoc;
      }
  
diff --git a/patches/api/Expand-Location-Manipulation-API.patch b/patches/api/Expand-Location-Manipulation-API.patch
index 16c282b42d..553d77767e 100644
--- a/patches/api/Expand-Location-Manipulation-API.patch
+++ b/patches/api/Expand-Location-Manipulation-API.patch
@@ -9,7 +9,7 @@ diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/L
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/Location.java
 +++ b/src/main/java/org/bukkit/Location.java
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
      public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
  
      // Paper start
diff --git a/patches/api/Fix-Spigot-annotation-mistakes.patch b/patches/api/Fix-Spigot-annotation-mistakes.patch
index e6ffef5c9d..b8b362d303 100644
--- a/patches/api/Fix-Spigot-annotation-mistakes.patch
+++ b/patches/api/Fix-Spigot-annotation-mistakes.patch
@@ -51,7 +51,7 @@ diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/L
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/Location.java
 +++ b/src/main/java/org/bukkit/Location.java
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
       * @param y The y-coordinate of this new location
       * @param z The z-coordinate of this new location
       */
@@ -60,7 +60,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          this(world, x, y, z, 0, 0);
      }
  
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
       * @param yaw The absolute rotation on the x-plane, in degrees
       * @param pitch The absolute rotation on the y-plane, in degrees
       */
@@ -69,7 +69,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          if (world != null) {
              this.world = new WeakReference<>(world);
          }
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
       * @throws IllegalArgumentException when world is unloaded
       * @see #isWorldLoaded()
       */
diff --git a/patches/api/Location.isChunkLoaded-API.patch b/patches/api/Location.isChunkLoaded-API.patch
index d3cf3b5347..7d1bdd4fe4 100644
--- a/patches/api/Location.isChunkLoaded-API.patch
+++ b/patches/api/Location.isChunkLoaded-API.patch
@@ -8,7 +8,7 @@ diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/L
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/Location.java
 +++ b/src/main/java/org/bukkit/Location.java
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
          return this;
      }
  
diff --git a/patches/api/Location.toBlockLocation-toCenterLocation.patch b/patches/api/Location.toBlockLocation-toCenterLocation.patch
index 6448bc01ea..1fb63da78e 100644
--- a/patches/api/Location.toBlockLocation-toCenterLocation.patch
+++ b/patches/api/Location.toBlockLocation-toCenterLocation.patch
@@ -9,7 +9,7 @@ diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/L
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/org/bukkit/Location.java
 +++ b/src/main/java/org/bukkit/Location.java
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
      }
  
      public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
diff --git a/patches/api/isChunkGenerated-API.patch b/patches/api/isChunkGenerated-API.patch
index 4579e9a6fa..1ceadf72f0 100644
--- a/patches/api/isChunkGenerated-API.patch
+++ b/patches/api/isChunkGenerated-API.patch
@@ -16,7 +16,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  import java.util.HashMap;
  import java.util.Map;
  import org.bukkit.block.Block;
-@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable {
+@@ -0,0 +0,0 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm
      public boolean isChunkLoaded() { return this.getWorld().isChunkLoaded(locToBlock(x) >> 4, locToBlock(z) >> 4); } // Paper
  
      // Paper start