Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>When this is true, the server falls back to spawning them in a different world at the same coordinates.</p>
*
* @return whether the player's saved world was invalid
*/
public boolean isInvalidWorld() {
return this.invalidWorld;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Newwind <support@newwindserver.com>
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<net.minecraft.world.level.Level> 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.<ServerPlayer.SavedPosition>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();
Loading