From b18d02087df4605ea090a30a46d1685f06ac20a2 Mon Sep 17 00:00:00 2001 From: Newwind Date: Sat, 6 Jun 2026 14:22:47 +0100 Subject: [PATCH 1/3] Add files via upload --- ...rld-to-Asyncplayerspawnlocationevent.patch | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch 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..ad3ff85f2d9c --- /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 5014840..91f219e 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(MinecraftServer server, com.mojang.authlib.GameProfile profile, net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) { + this.profile = profile; + this.listener = listener; +@@ -58,8 +59,8 @@ public class PrepareSpawnTask implements ConfigurationTask { + .map(compoundTag -> TagValueInput.create(scopedCollector, this.server.registryAccess(), compoundTag)); + // 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 = optional.isEmpty(); // New players don't have saved data! ++ this.invalidPlayerWorld = false; + net.minecraft.resources.ResourceKey resourceKey = null; // Paper +- boolean[] invalidPlayerWorld = {false}; + bukkitData: if (optional.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; +@@ -79,7 +80,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 savedPosition = optional.flatMap( +@@ -103,6 +104,7 @@ public class PrepareSpawnTask implements ConfigurationTask { + if (serverLevel1 == null) { + LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey); + serverLevel1 = vanillaDefaultLevel; ++ this.invalidPlayerWorld = true; + } + } + final ServerLevel serverLevel = serverLevel1; +@@ -211,7 +213,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(vec3final, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y), +- PrepareSpawnTask.this.newPlayer ++ PrepareSpawnTask.this.newPlayer, ++ PrepareSpawnTask.this.invalidPlayerWorld + ); + ev.callEvent(); + return ev.getSpawnLocation(); From 191e8055ccf3a3e0bade8c8a6aadaa4d18014e10 Mon Sep 17 00:00:00 2001 From: Newwind Date: Sat, 6 Jun 2026 14:23:19 +0100 Subject: [PATCH 2/3] Update AsyncPlayerSpawnLocationEvent.java --- .../player/AsyncPlayerSpawnLocationEvent.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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; } /** From 3c0c8f2e41d8dbf71e705fefc2bc81de009fb75e Mon Sep 17 00:00:00 2001 From: Newwind Date: Sat, 6 Jun 2026 14:43:48 +0100 Subject: [PATCH 3/3] update patch to 26.1.2 --- ...rld-to-Asyncplayerspawnlocationevent.patch | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch b/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch index ad3ff85f2d9c..ff5d6fa23608 100644 --- a/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch +++ b/paper-server/patches/features/0033-add-isInvalidPlayerWorld-to-Asyncplayerspawnlocationevent.patch @@ -7,7 +7,7 @@ If a world that players are in gets unloaded/reset, they end up spawning in the 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 5014840..91f219e 100644 +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 { @@ -15,20 +15,20 @@ index 5014840..91f219e 100644 private final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener; private boolean newPlayer; + private boolean invalidPlayerWorld; - public PrepareSpawnTask(MinecraftServer server, com.mojang.authlib.GameProfile profile, net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) { + + public PrepareSpawnTask(final MinecraftServer server, final com.mojang.authlib.GameProfile profile, final net.minecraft.server.network.ServerConfigurationPacketListenerImpl listener) { this.profile = profile; - this.listener = listener; -@@ -58,8 +59,8 @@ public class PrepareSpawnTask implements ConfigurationTask { - .map(compoundTag -> TagValueInput.create(scopedCollector, this.server.registryAccess(), compoundTag)); +@@ -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 = optional.isEmpty(); // New players don't have saved data! + 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 (optional.isPresent()) { + 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; -@@ -79,7 +80,7 @@ public class PrepareSpawnTask implements ConfigurationTask { +@@ -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; @@ -36,22 +36,22 @@ index 5014840..91f219e 100644 + this.invalidPlayerWorld = true; } } - ServerPlayer.SavedPosition savedPosition = optional.flatMap( -@@ -103,6 +104,7 @@ public class PrepareSpawnTask implements ConfigurationTask { - if (serverLevel1 == null) { + 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); - serverLevel1 = vanillaDefaultLevel; + spawnLevel = spawnDataLevel; + this.invalidPlayerWorld = true; } } - final ServerLevel serverLevel = serverLevel1; -@@ -211,7 +213,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(vec3final, this.spawnLevel, this.spawnAngle.x, this.spawnAngle.y), -- PrepareSpawnTask.this.newPlayer -+ PrepareSpawnTask.this.newPlayer, -+ PrepareSpawnTask.this.invalidPlayerWorld - ); - ev.callEvent(); - return ev.getSpawnLocation(); + 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();