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