diff --git a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java
index c749a898e9..6f83097b10 100644
--- a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java
+++ b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java
@@ -287,13 +287,28 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
// Paper end
/**
- * Gets the Location where the player will spawn at, null if they
+ * Gets the Location where the player will spawn at, {@code null} if they
+ * don't have a valid respawn point.
+ *
+ * Unlike online players, the location if found will not be loaded by default.
+ *
+ * @return respawn location if exists, otherwise {@code null}.
+ * @see #getRespawnLocation(boolean) for more fine-grained control over chunk loading and validation behaviour.
+ */
+ default @Nullable Location getRespawnLocation() {
+ return this.getRespawnLocation(false); // keep old behavior for offline players
+ }
+
+ /**
+ * Gets the Location where the player will spawn at, {@code null} if they
* don't have a valid respawn point.
*
- * @return respawn location if exists, otherwise null.
+ * @param loadLocationAndValidate load the expected respawn location to retrieve the exact position of the spawn
+ * block and check if this position is still valid or not. Loading the location
+ * will induce a sync chunk load and must hence be used with caution.
+ * @return respawn location if exists, otherwise {@code null}.
*/
- @Nullable
- public Location getRespawnLocation();
+ @Nullable Location getRespawnLocation(boolean loadLocationAndValidate);
/**
* Increments the given statistic for this player.
diff --git a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
index a4484ad3cb..e8d7c31576 100644
--- a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
+++ b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java
@@ -485,9 +485,11 @@ public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder
* to validate if the current respawn location is still valid.
*
* @return respawn location if exists, otherwise null.
+ * @deprecated this method doesn't take the respawn angle into account, use
+ * {@link Player#getRespawnLocation(boolean)} with loadLocationAndValidate = false instead
*/
- @Nullable
- Location getPotentialRespawnLocation();
+ @Deprecated(since = "1.21.5")
+ @Nullable Location getPotentialRespawnLocation();
/**
* @return the player's fishing hook if they are fishing
diff --git a/paper-api/src/main/java/org/bukkit/entity/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java
index 9ccb7e9016..ac341838b6 100644
--- a/paper-api/src/main/java/org/bukkit/entity/Player.java
+++ b/paper-api/src/main/java/org/bukkit/entity/Player.java
@@ -545,6 +545,20 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*/
public boolean isSleepingIgnored();
+ /**
+ * Gets the Location where the player will spawn at, {@code null} if they
+ * don't have a valid respawn point.
+ *
+ * Unlike offline players, the location if found will be loaded to validate by default.
+ *
+ * @return respawn location if exists, otherwise {@code null}.
+ * @see #getRespawnLocation(boolean) for more fine-grained control over chunk loading and validation behaviour.
+ */
+ @Override
+ default @Nullable Location getRespawnLocation() {
+ return this.getRespawnLocation(true);
+ }
+
/**
* Sets the Location where the player will spawn at their bed.
*
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 79e226da35..43ae147ae1 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -6,7 +6,6 @@ import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.GlobalPos;
@@ -33,8 +32,6 @@ import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.craftbukkit.util.CraftLocation;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
-import org.bukkit.metadata.MetadataValue;
-import org.bukkit.plugin.Plugin;
@SerializableAs("Player")
public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable {
@@ -362,18 +359,23 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
}
@Override
- public Location getRespawnLocation() {
- CompoundTag data = this.getData();
+ public Location getRespawnLocation(final boolean loadLocationAndValidate) {
+ final CompoundTag data = this.getData();
if (data == null) return null;
final ServerPlayer.RespawnConfig respawnConfig = data.read("respawn", ServerPlayer.RespawnConfig.CODEC).orElse(null);
- if (respawnConfig != null) {
- final ServerLevel level = this.server.console.getLevel(respawnConfig.dimension());
- if (level != null) {
- return CraftLocation.toBukkit(respawnConfig.pos(), level.getWorld(), respawnConfig.angle(), 0);
- }
+ if (respawnConfig == null) return null;
+
+ final ServerLevel level = this.server.console.getLevel(respawnConfig.dimension());
+ if (level == null) return null;
+
+ if (!loadLocationAndValidate) {
+ return CraftLocation.toBukkit(respawnConfig.pos(), level.getWorld(), respawnConfig.angle(), 0);
}
- return null;
+
+ return ServerPlayer.findRespawnAndUseSpawnBlock(level, respawnConfig, false)
+ .map(resolvedPos -> CraftLocation.toBukkit(resolvedPos.position(), level.getWorld(), resolvedPos.yaw(), 0))
+ .orElse(null);
}
private ServerStatsCounter getStatisticManager() {
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 df6612a86f..0d319b7c39 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
@@ -1541,19 +1541,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
@Override
- public Location getRespawnLocation() {
+ public Location getRespawnLocation(final boolean loadLocationAndValidate) {
final ServerPlayer.RespawnConfig respawnConfig = this.getHandle().getRespawnConfig();
if (respawnConfig == null) return null;
- ServerLevel world = this.getHandle().server.getLevel(respawnConfig.dimension());
- if (world != null) {
- Optional spawnLoc = ServerPlayer.findRespawnAndUseSpawnBlock(world, respawnConfig, true);
- if (spawnLoc.isPresent()) {
- ServerPlayer.RespawnPosAngle vec = spawnLoc.get();
- return CraftLocation.toBukkit(vec.position(), world.getWorld(), vec.yaw(), 0);
- }
+ final ServerLevel world = this.getHandle().server.getLevel(respawnConfig.dimension());
+ if (world == null) return null;
+
+ if (!loadLocationAndValidate) {
+ return CraftLocation.toBukkit(respawnConfig.pos(), world.getWorld(), respawnConfig.angle(), 0);
}
- return null;
+
+ return ServerPlayer.findRespawnAndUseSpawnBlock(world, respawnConfig, false)
+ .map(pos -> CraftLocation.toBukkit(pos.position(), world.getWorld(), pos.yaw(), 0))
+ .orElse(null);
}
@Override