diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPrepareTeleportEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPrepareTeleportEvent.java
new file mode 100644
index 000000000000..868d8a5054dd
--- /dev/null
+++ b/paper-api/src/main/java/io/papermc/paper/event/player/PlayerPrepareTeleportEvent.java
@@ -0,0 +1,98 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.entity.TeleportFlag;
+import java.util.Collections;
+import java.util.Set;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Event that is fired before attempting to teleport a player (Allows for pre-teleport handling, such as dismounting passengers if teleporting cross-worlds etc.)
+ *
+ * After the handling of this event, the player will attempt to teleport to the given location.
+ */
+@NullMarked
+public class PlayerPrepareTeleportEvent extends PlayerEvent {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final Location to;
+ private final PlayerTeleportEvent.TeleportCause cause;
+ private final Set teleportFlags;
+
+ @ApiStatus.Internal
+ public PlayerPrepareTeleportEvent(final Player player, Location to) {
+ super(player);
+ this.to = to;
+ this.cause = PlayerTeleportEvent.TeleportCause.PLUGIN;
+ this.teleportFlags = Collections.emptySet();
+ }
+
+ @ApiStatus.Internal
+ public PlayerPrepareTeleportEvent(final Player player, Location to, PlayerTeleportEvent.TeleportCause cause) {
+ super(player);
+ this.to = to;
+ this.cause = cause;
+ this.teleportFlags = Collections.emptySet();
+ }
+
+ @ApiStatus.Internal
+ public PlayerPrepareTeleportEvent(final Player player, Location to, PlayerTeleportEvent.TeleportCause cause, Set teleportFlags) {
+ super(player);
+ this.to = to;
+ this.cause = cause;
+ this.teleportFlags = Set.copyOf(teleportFlags);
+ }
+
+ /**
+ * Gets the location this player is teleporting to
+ *
+ * @return A copy of the Location the player is teleporting to
+ */
+ public Location getTo() {
+ return this.to.clone();
+ }
+
+ /**
+ * Gets the location this player is teleporting from
+ *
+ * @return A copy of the Location the player is moving from
+ */
+ public Location getFrom() {
+ return this.getPlayer().getLocation();
+ }
+
+ /**
+ * Returns the relative teleportation flags used in this teleportation.
+ * This determines which axis the player will not lose their velocity in.
+ *
+ * @return an immutable set of relative teleportation flags
+ */
+ public @Unmodifiable Set getRelativeTeleportationFlags() {
+ return this.teleportFlags;
+ }
+
+ /**
+ * Gets the cause of this teleportation event
+ *
+ * @return Cause of the event
+ */
+ public PlayerTeleportEvent.TeleportCause getCause() {
+ return this.cause;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 596338c7c9b3..44bd4c4e40a2 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -18,6 +18,7 @@
import io.papermc.paper.entity.LookAnchor;
import io.papermc.paper.entity.PaperPlayerGiveResult;
import io.papermc.paper.entity.PlayerGiveResult;
+import io.papermc.paper.event.player.PlayerPrepareTeleportEvent;
import io.papermc.paper.math.Angle;
import io.papermc.paper.math.Position;
import io.papermc.paper.util.MCUtil;
@@ -1316,6 +1317,12 @@ protected boolean teleport0(Location location, org.bukkit.event.player.PlayerTel
if (this.getHandle().connection == null) {
return false;
}
+
+ // Paper start - Add PlayerPrepareTeleportEvent
+ final PlayerPrepareTeleportEvent playerPrepareTeleportEvent = new PlayerPrepareTeleportEvent(this, location, cause, Set.of(flags));
+ this.server.getPluginManager().callEvent(playerPrepareTeleportEvent);
+ // Paper end - Add PlayerPrepareTeleportEvent
+
// Minecraft does not currently support teleporting players between worlds with passengers.
// It causes them to be dismounted, and causes weird behavior.
if (location.getWorld() != this.getWorld() && this.getHandle().isVehicle()) {