diff --git a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java index 5622fe3165ba..3da609059c4a 100644 --- a/paper-api/src/main/java/org/bukkit/OfflinePlayer.java +++ b/paper-api/src/main/java/org/bukkit/OfflinePlayer.java @@ -264,6 +264,7 @@ public default BanEntry banPlayer(@Nullable String reason, @Nullable java.util.D @Nullable @Deprecated(since = "1.20.4") public Location getBedSpawnLocation(); + // Paper start /** * Gets the last date and time that this player logged into the server. @@ -290,13 +291,28 @@ public default BanEntry banPlayer(@Nullable String reason, @Nullable java.util.D // 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}. + */ + @Nullable + default 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 load load the current location to retrieve the exact position of the spawn block + * and check if this position is still valid or not + * @return respawn location if exists, otherwise {@code null}. */ @Nullable - public Location getRespawnLocation(); + Location getRespawnLocation(boolean load); /** * 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 34538999ef88..8a4259d2bec0 100644 --- a/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java +++ b/paper-api/src/main/java/org/bukkit/entity/HumanEntity.java @@ -469,9 +469,11 @@ default Location getPotentialBedLocation() { * to validate if the current respawn location is still valid. * * @return respawn location if exists, otherwise null. + * @deprecated this method doesn't take in account the respawn angle, use + * {@link Player#getRespawnLocation(boolean)} with load = false instead */ - @Nullable - Location getPotentialRespawnLocation(); + @Deprecated(since = "1.21.4") + @Nullable Location getPotentialRespawnLocation(); // Paper end // Paper start /** 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 7d21ee64c9b9..fe802ce5266d 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Player.java +++ b/paper-api/src/main/java/org/bukkit/entity/Player.java @@ -560,15 +560,11 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @Deprecated(since = "1.20.4") public Location getBedSpawnLocation(); - /** - * Gets the Location where the player will spawn at, null if they - * don't have a valid respawn point. - * - * @return respawn location if exists, otherwise null. - */ @Nullable @Override - public Location getRespawnLocation(); + default 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 94ca0407303c..5db986b1cfb1 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -13,6 +13,8 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.UserWhiteListEntry; import net.minecraft.stats.ServerStatsCounter; import net.minecraft.world.level.storage.PlayerDataStorage; @@ -28,12 +30,11 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.SerializableAs; import org.bukkit.craftbukkit.entity.memory.CraftMemoryMapper; -import org.bukkit.craftbukkit.profile.CraftPlayerProfile; +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; -import org.bukkit.profile.PlayerProfile; @SerializableAs("Player") public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable { @@ -392,25 +393,35 @@ public Location getBedSpawnLocation() { } @Override - public Location getRespawnLocation() { + public Location getRespawnLocation(boolean load) { CompoundTag data = this.getData(); if (data == null) return null; - if (data.contains("SpawnX") && data.contains("SpawnY") && data.contains("SpawnZ")) { - // Paper start - fix wrong world - final float respawnAngle = data.getFloat("SpawnAngle"); - org.bukkit.World spawnWorld = this.server.getWorld(data.getString("SpawnWorld")); // legacy + if (data.contains("SpawnX", Tag.TAG_ANY_NUMERIC) && data.contains("SpawnY", Tag.TAG_ANY_NUMERIC) && data.contains("SpawnZ", Tag.TAG_ANY_NUMERIC)) { + org.bukkit.World spawnWorld = null; if (data.contains("SpawnDimension")) { com.mojang.serialization.DataResult> result = net.minecraft.world.level.Level.RESOURCE_KEY_CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, data.get("SpawnDimension")); net.minecraft.resources.ResourceKey levelKey = result.resultOrPartial(LOGGER::error).orElse(net.minecraft.world.level.Level.OVERWORLD); net.minecraft.server.level.ServerLevel level = this.server.console.getLevel(levelKey); - spawnWorld = level != null ? level.getWorld() : spawnWorld; + spawnWorld = level != null ? level.getWorld() : null; + } + if (spawnWorld == null && data.contains("SpawnWorld")) { // legacy + spawnWorld = this.server.getWorld(data.getString("SpawnWorld")); } if (spawnWorld == null) { return null; } - return new Location(spawnWorld, data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ"), respawnAngle, 0); - // Paper end + + final float respawnAngle = data.getFloat("SpawnAngle"); + net.minecraft.core.BlockPos respawnPos = new net.minecraft.core.BlockPos(data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ")); + if (!load) { + return CraftLocation.toBukkit(respawnPos, spawnWorld, respawnAngle, 0); + } + + final org.bukkit.World finalSpawnWorld = spawnWorld; + return ServerPlayer.findRespawnAndUseSpawnBlock(((CraftWorld) spawnWorld).getHandle(), respawnPos, respawnAngle, data.getBoolean("SpawnForced"), false) + .map(resolvedPos -> CraftLocation.toBukkit(resolvedPos.position(), finalSpawnWorld, resolvedPos.yaw(), 0)) + .orElse(null); } return null; } 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 98fc89cc7a71..5de2cfbcdff8 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 @@ -167,7 +167,6 @@ import org.bukkit.craftbukkit.map.RenderData; import org.bukkit.craftbukkit.potion.CraftPotionEffectType; import org.bukkit.craftbukkit.potion.CraftPotionUtil; -import org.bukkit.craftbukkit.profile.CraftPlayerProfile; import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftLocation; @@ -181,7 +180,6 @@ import org.bukkit.event.player.PlayerHideEntityEvent; import org.bukkit.event.player.PlayerRegisterChannelEvent; import org.bukkit.event.player.PlayerShowEntityEvent; -import org.bukkit.event.player.PlayerSpawnChangeEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerUnregisterChannelEvent; import org.bukkit.inventory.EquipmentSlot; @@ -194,7 +192,6 @@ import org.bukkit.plugin.messaging.StandardMessenger; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.profile.PlayerProfile; import org.bukkit.scoreboard.Scoreboard; import org.jetbrains.annotations.NotNull; @@ -1556,16 +1553,16 @@ public Location getBedSpawnLocation() { } @Override - public Location getRespawnLocation() { + public Location getRespawnLocation(boolean load) { ServerLevel world = this.getHandle().server.getLevel(this.getHandle().getRespawnDimension()); - BlockPos bed = this.getHandle().getRespawnPosition(); + BlockPos respawnPos = this.getHandle().getRespawnPosition(); - if (world != null && bed != null) { - Optional spawnLoc = ServerPlayer.findRespawnAndUseSpawnBlock(world, bed, this.getHandle().getRespawnAngle(), this.getHandle().isRespawnForced(), true); - if (spawnLoc.isPresent()) { - ServerPlayer.RespawnPosAngle vec = spawnLoc.get(); - return CraftLocation.toBukkit(vec.position(), world.getWorld(), vec.yaw(), 0); + if (world != null && respawnPos != null) { + if (!load) { + return CraftLocation.toBukkit(respawnPos, world.getWorld(), this.getHandle().getRespawnAngle(), 0); } + return ServerPlayer.findRespawnAndUseSpawnBlock(world, respawnPos, this.getHandle().getRespawnAngle(), this.getHandle().isRespawnForced(), false) + .map(resolvedPos -> CraftLocation.toBukkit(resolvedPos.position(), world.getWorld(), resolvedPos.yaw(), 0)).orElse(null); } return null; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java index a8b46ea5e4b6..cc2b34f41a0c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftLocation.java @@ -28,6 +28,7 @@ public static Location toBukkit(BlockPos blockPosition) { public static Location toBukkit(BlockPos blockPosition, net.minecraft.world.level.Level world) { return CraftLocation.toBukkit(blockPosition, world.getWorld(), 0.0F, 0.0F); } + public static Location toBukkit(BlockPos blockPosition, World world) { return CraftLocation.toBukkit(blockPosition, world, 0.0F, 0.0F); } @@ -40,16 +41,14 @@ public static BlockPos toBlockPosition(Location location) { return new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); } - // Paper start public static net.minecraft.core.GlobalPos toGlobalPos(Location location) { return net.minecraft.core.GlobalPos.of(((org.bukkit.craftbukkit.CraftWorld) location.getWorld()).getHandle().dimension(), toBlockPosition(location)); } public static Location fromGlobalPos(net.minecraft.core.GlobalPos globalPos) { BlockPos pos = globalPos.pos(); - return new org.bukkit.Location(net.minecraft.server.MinecraftServer.getServer().getLevel(globalPos.dimension()).getWorld(), pos.getX(), pos.getY(), pos.getZ()); + return new Location(net.minecraft.server.MinecraftServer.getServer().getLevel(globalPos.dimension()).getWorld(), pos.getX(), pos.getY(), pos.getZ()); } - // Paper end public static Vec3 toVec3D(Location location) { return new Vec3(location.getX(), location.getY(), location.getZ());