diff --git a/paper-api/src/main/java/io/papermc/paper/event/player/AsyncPlayerSpawnLocationEvent.java b/paper-api/src/main/java/io/papermc/paper/event/player/AsyncPlayerSpawnLocationEvent.java
index 7945e9e3388d..00736f9fa6d8 100644
--- a/paper-api/src/main/java/io/papermc/paper/event/player/AsyncPlayerSpawnLocationEvent.java
+++ b/paper-api/src/main/java/io/papermc/paper/event/player/AsyncPlayerSpawnLocationEvent.java
@@ -23,14 +23,32 @@ public class AsyncPlayerSpawnLocationEvent extends Event {
private final PlayerConfigurationConnection connection;
private final boolean newPlayer;
+ private final boolean invalidWorld;
private Location spawnLocation;
@ApiStatus.Internal
public AsyncPlayerSpawnLocationEvent(final PlayerConfigurationConnection connection, final Location spawnLocation, final boolean newPlayer) {
+ this(connection, spawnLocation, newPlayer, false);
+ }
+
+ @ApiStatus.Internal
+ public AsyncPlayerSpawnLocationEvent(final PlayerConfigurationConnection connection, final Location spawnLocation, final boolean newPlayer, final boolean invalidWorld) {
super(true);
this.connection = connection;
this.spawnLocation = spawnLocation;
this.newPlayer = newPlayer;
+ this.invalidWorld = invalidWorld;
+ }
+
+ /**
+ * Returns true if the player's saved data referenced a Bukkit world that is no longer loaded or available.
+ *
+ *
When this is true, the server falls back to spawning them in a different world at the same coordinates.
+ *
+ * @return whether the player's saved world was invalid
+ */
+ public boolean isInvalidWorld() {
+ return this.invalidWorld;
}
/**
diff --git a/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch b/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch
new file mode 100644
index 000000000000..ff5d6fa23608
--- /dev/null
+++ b/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch
@@ -0,0 +1,57 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Newwind
+Date: Sat, 6 Jun 2026 18:00:37 +0200
+Subject: [PATCH] Add isInvalidWorld to AsyncPlayerSpawnLocationEvent
+
+If a world that players are in gets unloaded/reset, they end up spawning in the overworld at the same coordinates they were at, this can cause players die suffocate in walls or fall to their death
+There is no easy way to detect if a player spawn location has had its world changed, this patch fixes that by tracking if the world was determined to be invalid.
+
+diff --git a/net/minecraft/server/network/config/PrepareSpawnTask.java b/net/minecraft/server/network/config/PrepareSpawnTask.java
+index f087b290..ac47509a 100644
+--- a/net/minecraft/server/network/config/PrepareSpawnTask.java
++++ b/net/minecraft/server/network/config/PrepareSpawnTask.java
+@@ -40,6 +40,7 @@ public class PrepareSpawnTask implements ConfigurationTask {
+ private final com.mojang.authlib.GameProfile profile;
+ private final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener;
+ private boolean newPlayer;
++ private boolean invalidPlayerWorld;
+
+ public PrepareSpawnTask(final MinecraftServer server, final com.mojang.authlib.GameProfile profile, final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) {
+ this.profile = profile;
+@@ -59,8 +60,8 @@ public class PrepareSpawnTask implements ConfigurationTask {
+ .map(tag -> TagValueInput.create(reporter, this.server.registryAccess(), tag));
+ // Paper start - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found
+ this.newPlayer = loadedData.isEmpty(); // New players don't have saved data!
++ this.invalidPlayerWorld = false;
+ net.minecraft.resources.ResourceKey resourceKey = null; // Paper
+- boolean[] invalidPlayerWorld = {false};
+ bukkitData: if (loadedData.isPresent()) {
+ // The main way for bukkit worlds to store the world is the world UUID despite mojang adding custom worlds
+ final org.bukkit.World bWorld;
+@@ -80,7 +81,7 @@ public class PrepareSpawnTask implements ConfigurationTask {
+ resourceKey = ((org.bukkit.craftbukkit.CraftWorld) bWorld).getHandle().dimension();
+ } else {
+ resourceKey = net.minecraft.world.level.Level.OVERWORLD;
+- invalidPlayerWorld[0] = true;
++ this.invalidPlayerWorld = true;
+ }
+ }
+ ServerPlayer.SavedPosition loadedPosition = loadedData.flatMap(tag -> tag.read(ServerPlayer.SavedPosition.MAP_CODEC))
+@@ -102,6 +103,7 @@ public class PrepareSpawnTask implements ConfigurationTask {
+ if (spawnLevel == null) {
+ LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey);
+ spawnLevel = spawnDataLevel;
++ this.invalidPlayerWorld = true;
+ }
+ }
+ final ServerLevel finalSpawnLevel = spawnLevel;
+@@ -215,7 +217,8 @@ public class PrepareSpawnTask implements ConfigurationTask {
+ io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent ev = new io.papermc.paper.event.player.AsyncPlayerSpawnLocationEvent(
+ PrepareSpawnTask.this.listener.paperConnection,
+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(spawnPositionFinal, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y),
+- PrepareSpawnTask.this.newPlayer
++ PrepareSpawnTask.this.newPlayer,
++ PrepareSpawnTask.this.invalidPlayerWorld
+ );
+ ev.callEvent();
+ return ev.getSpawnLocation();