From 19a36413c721cdd3a7a7937e388e22b8f545fa04 Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Sat, 31 May 2025 14:44:19 +0200 Subject: [PATCH] Last per file patches --- .../StructureTemplate.java.patch | 150 ----- .../server/players/PlayerList.java.patch | 623 +++++++++--------- .../StructureTemplate.java.patch | 142 ++++ 3 files changed, 451 insertions(+), 464 deletions(-) delete mode 100644 paper-server/patches/rejected/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch rename paper-server/patches/{rejected => sources}/net/minecraft/server/players/PlayerList.java.patch (73%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch diff --git a/paper-server/patches/rejected/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch b/paper-server/patches/rejected/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch deleted file mode 100644 index b36443f912..0000000000 --- a/paper-server/patches/rejected/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch +++ /dev/null @@ -1,150 +0,0 @@ -From 09671551669244ef4f259d8b27547e463d6795d4 Mon Sep 17 00:00:00 2001 -From: File -Date: Sun, 20 Apr 1997 15:37:42 +0200 -Subject: [PATCH] paper File Patches - - -diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java -index b7965fd610b90f5674aae3571ab9a35e29d3d4f5..bb8b5ca9044ecb33687e7f20cfb0acbf55f887c7 100644 ---- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java -+++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java -@@ -73,6 +73,10 @@ public class StructureTemplate { - public final List entityInfoList = Lists.newArrayList(); - private Vec3i size = Vec3i.ZERO; - private String author = "?"; -+ // CraftBukkit start - data containers -+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); -+ public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(StructureTemplate.DATA_TYPE_REGISTRY); -+ // CraftBukkit end - - public Vec3i getSize() { - return this.size; -@@ -247,6 +251,19 @@ public class StructureTemplate { - if (this.palettes.isEmpty()) { - return false; - } else { -+ // CraftBukkit start -+ // We only want the TransformerGeneratorAccess at certain locations because in here are many "block update" calls that shouldn't be transformed -+ ServerLevelAccessor wrappedAccess = serverLevel; -+ org.bukkit.craftbukkit.util.CraftStructureTransformer structureTransformer = null; -+ if (wrappedAccess instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { -+ serverLevel = transformerAccess.getDelegate(); -+ structureTransformer = transformerAccess.getStructureTransformer(); -+ // The structureTransformer is not needed if we can not transform blocks therefore we can save a little bit of performance doing this -+ if (structureTransformer != null && !structureTransformer.canTransformBlocks()) { -+ structureTransformer = null; -+ } -+ } -+ // CraftBukkit end - List list = settings.getRandomPalette(this.palettes, offset).blocks(); - if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty()) - && this.size.getX() >= 1 -@@ -272,6 +289,21 @@ public class StructureTemplate { - serverLevel.setBlock(blockPos, Blocks.BARRIER.defaultBlockState(), 820); - } - -+ // CraftBukkit start -+ if (structureTransformer != null) { -+ org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(serverLevel, blockPos, blockState, null); -+ if (structureBlockInfo.nbt != null && craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState entityState) { -+ entityState.loadData(structureBlockInfo.nbt); -+ if (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftLootable craftLootable) { -+ craftLootable.setSeed(random.nextLong()); -+ } -+ } -+ craftBlockState = structureTransformer.transformCraftState(craftBlockState); -+ blockState = craftBlockState.getHandle(); -+ structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos, blockState, (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState ? craftBlockEntityState.getSnapshotNBT() : null)); -+ } -+ // CraftBukkit end -+ - if (serverLevel.setBlock(blockPos, blockState, flags)) { - i = Math.min(i, blockPos.getX()); - i1 = Math.min(i1, blockPos.getY()); -@@ -283,7 +315,7 @@ public class StructureTemplate { - if (structureBlockInfo.nbt != null) { - BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos); - if (blockEntity != null) { -- if (blockEntity instanceof RandomizableContainer) { -+ if (structureTransformer == null && blockEntity instanceof RandomizableContainer) { // CraftBukkit - only process if don't have a transformer access (Was already set above) - SPIGOT-7520: Use structureTransformer as check, so that it is the same as above - structureBlockInfo.nbt.putLong("LootTableSeed", random.nextLong()); - } - -@@ -366,7 +398,11 @@ public class StructureTemplate { - if (pair1.getSecond() != null) { - BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos4); - if (blockEntity != null) { -- blockEntity.setChanged(); -+ // Paper start - Fix NBT pieces overriding a block entity during worldgen deadlock -+ if (!(serverLevel instanceof net.minecraft.world.level.WorldGenLevel)) { -+ blockEntity.setChanged(); -+ } -+ // Paper end - Fix NBT pieces overriding a block entity during worldgen deadlock - } - } - } -@@ -374,7 +410,7 @@ public class StructureTemplate { - - if (!settings.isIgnoreEntities()) { - this.placeEntities( -- serverLevel, -+ wrappedAccess, // CraftBukkit - offset, - settings.getMirror(), - settings.getRotation(), -@@ -488,14 +524,17 @@ public class StructureTemplate { - ); - } - } -+ - } - - private static Optional createEntityIgnoreException(ServerLevelAccessor level, CompoundTag tag) { -- try { -- return EntityType.create(tag, level.getLevel(), EntitySpawnReason.STRUCTURE); -- } catch (Exception var3) { -- return Optional.empty(); -- } -+ // CraftBukkit start -+ // try { -+ return EntityType.create(tag, level.getLevel(), EntitySpawnReason.STRUCTURE, true); // Paper - Don't fire sync event during generation -+ // } catch (Exception var3) { -+ // return Optional.empty(); -+ // } -+ // CraftBukkit end - } - - public Vec3i getSize(Rotation rotation) { -@@ -688,6 +727,11 @@ public class StructureTemplate { - - tag.put("entities", listTag3); - tag.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ())); -+ // CraftBukkit start - PDC -+ if (!this.persistentDataContainer.isEmpty()) { -+ tag.put("BukkitValues", this.persistentDataContainer.toTagCompound()); -+ } -+ // CraftBukkit end - return NbtUtils.addCurrentDataVersion(tag); - } - -@@ -718,6 +762,11 @@ public class StructureTemplate { - .ifPresent(compoundTag1 -> this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(vec3, blockPos, compoundTag1))); - } - ); -+ // CraftBukkit start - PDC -+ if (tag.get("BukkitValues") instanceof CompoundTag compoundTag) { -+ this.persistentDataContainer.putAll(compoundTag); -+ } -+ // CraftBukkit end - } - - private void loadPalette(HolderGetter blockGetter, ListTag paletteTag, ListTag blocksTag) { -@@ -817,7 +866,7 @@ public class StructureTemplate { - - public static final class Palette { - private final List blocks; -- private final Map> cache = Maps.newHashMap(); -+ private final Map> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads - @Nullable - private List cachedJigsaws; - diff --git a/paper-server/patches/rejected/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch similarity index 73% rename from paper-server/patches/rejected/net/minecraft/server/players/PlayerList.java.patch rename to paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 7e92e2af0c..c16a12d4ea 100644 --- a/paper-server/patches/rejected/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -1,14 +1,6 @@ -From 09671551669244ef4f259d8b27547e463d6795d4 Mon Sep 17 00:00:00 2001 -From: File -Date: Sun, 20 Apr 1997 15:37:42 +0200 -Subject: [PATCH] paper File Patches - - -diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6a3dc0dd0 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java -@@ -112,14 +112,16 @@ public abstract class PlayerList { +@@ -111,14 +_,16 @@ private static final int SEND_PLAYER_INFO_INTERVAL = 600; private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); private final MinecraftServer server; @@ -28,7 +20,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 public final PlayerDataStorage playerIo; private boolean doWhiteList; private final LayeredRegistryAccess registries; -@@ -130,14 +132,26 @@ public abstract class PlayerList { +@@ -129,14 +_,26 @@ private static final boolean ALLOW_LOGOUTIVATOR = false; private int sendAllPlayerInfoIn; @@ -55,259 +47,264 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 GameProfile gameProfile = player.getGameProfile(); GameProfileCache profileCache = this.server.getProfileCache(); String string; -@@ -150,30 +164,94 @@ public abstract class PlayerList { +@@ -149,18 +_,69 @@ } - Optional optional = this.load(player); -- ResourceKey resourceKey = optional.>flatMap( -- compoundTag -> DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, compoundTag.get("Dimension"))).resultOrPartial(LOGGER::error) -+ // CraftBukkit start - Better rename detection -+ if (optional.isPresent()) { -+ string = optional.flatMap(t -> t.getCompound("bukkit")).flatMap(t -> t.getString("lastKnownName")).orElse(string); -+ } -+ // CraftBukkit end -+ // 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 -+ 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; -+ final CompoundTag playerData = optional.get(); -+ // TODO maybe convert this to a codec and use compoundTag#read, we need silent variants of that method first. -+ final Optional worldUUIDMost = playerData.getLong("WorldUUIDMost"); -+ final Optional worldUUIDLeast = playerData.getLong("WorldUUIDLeast"); -+ final java.util.Optional worldName = playerData.getString("world"); -+ if (worldUUIDMost.isPresent() && worldUUIDLeast.isPresent()) { -+ bWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(worldUUIDMost.get(), worldUUIDLeast.get())); -+ } else if (worldName.isPresent()) { // Paper - legacy bukkit world name -+ bWorld = org.bukkit.Bukkit.getServer().getWorld(worldName.get()); -+ } else { -+ break bukkitData; // if neither of the bukkit data points exist, proceed to the vanilla migration section + try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), LOGGER)) { +- Optional optional1 = this.load(player, scopedCollector); +- ResourceKey resourceKey = optional1.>flatMap(valueInput -> valueInput.read("Dimension", Level.RESOURCE_KEY_CODEC)) +- .orElse(Level.OVERWORLD); ++ Optional optional1 = this.load(player, scopedCollector); final Optional loadedPlayerData = optional1; // Paper - OBFHELPER ++ // CraftBukkit start - Better rename detection ++ if (loadedPlayerData.isPresent()) { ++ string = loadedPlayerData.flatMap(t -> t.child("bukkit")).flatMap(t -> t.getString("lastKnownName")).orElse(string); + } -+ if (bWorld != null) { -+ resourceKey = ((org.bukkit.craftbukkit.CraftWorld) bWorld).getHandle().dimension(); -+ } else { -+ resourceKey = Level.OVERWORLD; -+ invalidPlayerWorld[0] = true; -+ } -+ } -+ if (resourceKey == null) { // only run the vanilla logic if we haven't found a world from the bukkit data -+ // Below is the vanilla way of getting the dimension, this is for migration from vanilla servers -+ resourceKey = optional.>flatMap( -+ compoundTag -> { -+ com.mojang.serialization.DataResult> dataResult = DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, compoundTag.get("Dimension"))); -+ final Optional> result = dataResult.resultOrPartial(LOGGER::error); -+ invalidPlayerWorld[0] = result.isEmpty(); // reset to main world spawn if no valid world is found -+ return result; -+ } - ) -- .orElse(Level.OVERWORLD); -+ .orElse(Level.OVERWORLD); // revert to vanilla default main world, this isn't an "invalid world" since no player data existed -+ } -+ // Paper end - ServerLevel level = this.server.getLevel(resourceKey); - ServerLevel serverLevel; - if (level == null) { - LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey); - serverLevel = this.server.overworld(); -+ invalidPlayerWorld[0] = true; // Paper - reset to main world if no world with parsed value is found - } else { - serverLevel = level; - } - -+ // Paper start - Entity#getEntitySpawnReason -+ if (optional.isEmpty()) { -+ player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login -+ // Paper start - reset to main world spawn if first spawn or invalid world -+ } -+ if (optional.isEmpty() || invalidPlayerWorld[0]) { -+ // Paper end - reset to main world spawn if first spawn or invalid world -+ player.snapTo(player.adjustSpawnLocation(serverLevel, serverLevel.getSharedSpawnPos()).getBottomCenter(), serverLevel.getSharedSpawnAngle(), 0.0F); // Paper - MC-200092 - fix first spawn pos yaw being ignored -+ } -+ // Paper end - Entity#getEntitySpawnReason - player.setServerLevel(serverLevel); - String loggableAddress = connection.getLoggableAddress(this.server.logIPs()); -- LOGGER.info( -- "{}[{}] logged in with entity id {} at ({}, {}, {})", -- player.getName().getString(), -- loggableAddress, -- player.getId(), -- player.getX(), -- player.getY(), -- player.getZ() -- ); -+ // Spigot start - spawn location event -+ org.bukkit.entity.Player spawnPlayer = player.getBukkitEntity(); -+ org.spigotmc.event.player.PlayerSpawnLocationEvent ev = new org.spigotmc.event.player.PlayerSpawnLocationEvent(spawnPlayer, spawnPlayer.getLocation()); -+ this.cserver.getPluginManager().callEvent(ev); -+ -+ org.bukkit.Location loc = ev.getSpawnLocation(); -+ serverLevel = ((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle(); -+ -+ player.spawnIn(serverLevel); -+ // Paper start - set raw so we aren't fully joined to the world (not added to chunk or world) -+ player.setPosRaw(loc.getX(), loc.getY(), loc.getZ()); -+ player.setRot(loc.getYaw(), loc.getPitch()); -+ // Paper end - set raw so we aren't fully joined to the world -+ // Spigot end -+ // LOGGER.info( // CraftBukkit - Moved message to after join -+ // "{}[{}] logged in with entity id {} at ({}, {}, {})", -+ // player.getName().getString(), -+ // loggableAddress, -+ // player.getId(), -+ // player.getX(), -+ // player.getY(), -+ // player.getZ() -+ // ); - LevelData levelData = serverLevel.getLevelData(); - player.loadGameTypes(optional.orElse(null)); - ServerGamePacketListenerImpl serverGamePacketListenerImpl = new ServerGamePacketListenerImpl(this.server, connection, player, cookie); -@@ -191,8 +269,8 @@ public abstract class PlayerList { - levelData.isHardcore(), - this.server.levelKeys(), - this.getMaxPlayers(), -- this.viewDistance, -- this.simulationDistance, -+ serverLevel.spigotConfig.viewDistance,// Spigot - view distance -+ serverLevel.spigotConfig.simulationDistance, - _boolean1, - !_boolean, - _boolean2, -@@ -200,6 +278,7 @@ public abstract class PlayerList { - this.server.enforceSecureProfile() - ) - ); -+ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit - serverGamePacketListenerImpl.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); - serverGamePacketListenerImpl.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); - serverGamePacketListenerImpl.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); -@@ -219,26 +298,119 @@ public abstract class PlayerList { - mutableComponent = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), string); - } - -- this.broadcastSystemMessage(mutableComponent.withStyle(ChatFormatting.YELLOW), false); -+ // CraftBukkit start -+ mutableComponent.withStyle(ChatFormatting.YELLOW); -+ Component joinMessage = mutableComponent; // Paper - Adventure - serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); - ServerStatus status = this.server.getStatus(); - if (status != null && !cookie.transferred()) { - player.sendServerStatus(status); - } - -- player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); -+ // player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below - this.players.add(player); -+ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot - this.playersByUUID.put(player.getUUID(), player); -- this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); -- this.sendLevelInfo(player, serverLevel); -+ // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below -+ // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks -+ player.supressTrackerForLogin = true; - serverLevel.addNewPlayer(player); -- this.server.getCustomBossEvents().onPlayerConnect(player); -- this.sendActivePlayerEffects(player); -+ this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player); -+ // Paper end - Fire PlayerJoinEvent when Player is actually ready - optional.ifPresent(compoundTag -> { - player.loadAndSpawnEnderPearls(compoundTag); - player.loadAndSpawnParentVehicle(compoundTag); - }); -+ // CraftBukkit start -+ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity(); -+ -+ // Ensure that player inventory is populated with its viewer -+ player.containerMenu.transferTo(player.containerMenu, bukkitPlayer); -+ -+ org.bukkit.event.player.PlayerJoinEvent playerJoinEvent = new org.bukkit.event.player.PlayerJoinEvent(bukkitPlayer, io.papermc.paper.adventure.PaperAdventure.asAdventure(mutableComponent)); // Paper - Adventure -+ this.cserver.getPluginManager().callEvent(playerJoinEvent); -+ -+ if (!player.connection.isAcceptingMessages()) { -+ return; -+ } -+ -+ final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage(); -+ -+ if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure -+ joinMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(jm); // Paper - Adventure -+ this.server.getPlayerList().broadcastSystemMessage(joinMessage, false); // Paper - Adventure -+ } -+ // CraftBukkit end -+ -+ // CraftBukkit start - sendAll above replaced with this loop -+ ClientboundPlayerInfoUpdatePacket packet = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player)); // Paper - Add Listing API for Player -+ -+ final List onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1); // Paper - Use single player info update packet on join -+ for (int i = 0; i < this.players.size(); ++i) { -+ ServerPlayer entityplayer1 = (ServerPlayer) this.players.get(i); -+ -+ if (entityplayer1.getBukkitEntity().canSee(bukkitPlayer)) { -+ // Paper start - Add Listing API for Player -+ if (entityplayer1.getBukkitEntity().isListed(bukkitPlayer)) { -+ // Paper end - Add Listing API for Player -+ entityplayer1.connection.send(packet); -+ // Paper start - Add Listing API for Player ++ // CraftBukkit end ++ // 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 ++ ResourceKey resourceKey = null; // Paper ++ boolean[] invalidPlayerWorld = {false}; ++ bukkitData: if (loadedPlayerData.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; ++ final ValueInput playerData = loadedPlayerData.get(); ++ // TODO maybe convert this to a codec and use compoundTag#read, we need silent variants of that method first. ++ final Optional worldUUIDMost = playerData.getLong("WorldUUIDMost"); ++ final Optional worldUUIDLeast = playerData.getLong("WorldUUIDLeast"); ++ final java.util.Optional worldName = playerData.getString("world"); ++ if (worldUUIDMost.isPresent() && worldUUIDLeast.isPresent()) { ++ bWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(worldUUIDMost.get(), worldUUIDLeast.get())); ++ } else if (worldName.isPresent()) { // Paper - legacy bukkit world name ++ bWorld = org.bukkit.Bukkit.getServer().getWorld(worldName.get()); + } else { -+ entityplayer1.connection.send(ClientboundPlayerInfoUpdatePacket.createSinglePlayerInitializing(player, false)); ++ break bukkitData; // if neither of the bukkit data points exist, proceed to the vanilla migration section + } -+ // Paper end - Add Listing API for Player ++ if (bWorld != null) { ++ resourceKey = ((org.bukkit.craftbukkit.CraftWorld) bWorld).getHandle().dimension(); ++ } else { ++ resourceKey = Level.OVERWORLD; ++ invalidPlayerWorld[0] = true; ++ } ++ } ++ if (resourceKey == null) { // only run the vanilla logic if we haven't found a world from the bukkit data ++ // Below is the vanilla way of getting the dimension, this is for migration from vanilla servers ++ resourceKey = loadedPlayerData.>flatMap( ++ compoundTag -> { ++ Optional> result = compoundTag.read("Dimension", Level.RESOURCE_KEY_CODEC); ++ invalidPlayerWorld[0] = result.isEmpty(); // reset to main world spawn if no valid world is found ++ return result; ++ } ++ ) ++ .orElse(Level.OVERWORLD); // revert to vanilla default main world, this isn't an "invalid world" since no player data existed ++ } ++ // Paper end + ServerLevel level = this.server.getLevel(resourceKey); + ServerLevel serverLevel; + if (level == null) { + LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourceKey); + serverLevel = this.server.overworld(); ++ invalidPlayerWorld[0] = true; // Paper - reset to main world if no world with parsed value is found + } else { + serverLevel = level; + } + ++ // Paper start - Entity#getEntitySpawnReason ++ if (loadedPlayerData.isEmpty()) { ++ player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login ++ // Paper start - reset to main world spawn if first spawn or invalid world ++ } ++ if (loadedPlayerData.isEmpty() || invalidPlayerWorld[0]) { ++ // Paper end - reset to main world spawn if first spawn or invalid world ++ player.snapTo(player.adjustSpawnLocation(serverLevel, serverLevel.getSharedSpawnPos()).getBottomCenter(), serverLevel.getSharedSpawnAngle(), 0.0F); // Paper - MC-200092 - fix first spawn pos yaw being ignored ++ } ++ // Paper end - Entity#getEntitySpawnReason + player.setServerLevel(serverLevel); + if (optional1.isEmpty()) { + player.snapTo( +@@ -170,15 +_,29 @@ + + serverLevel.waitForChunkAndEntities(player.chunkPosition(), 1); + String loggableAddress = connection.getLoggableAddress(this.server.logIPs()); +- LOGGER.info( +- "{}[{}] logged in with entity id {} at ({}, {}, {})", +- player.getName().getString(), +- loggableAddress, +- player.getId(), +- player.getX(), +- player.getY(), +- player.getZ() +- ); ++ // Spigot start - spawn location event ++ org.bukkit.entity.Player spawnPlayer = player.getBukkitEntity(); ++ org.spigotmc.event.player.PlayerSpawnLocationEvent ev = new org.spigotmc.event.player.PlayerSpawnLocationEvent(spawnPlayer, spawnPlayer.getLocation()); ++ this.cserver.getPluginManager().callEvent(ev); ++ ++ org.bukkit.Location loc = ev.getSpawnLocation(); ++ serverLevel = ((org.bukkit.craftbukkit.CraftWorld) loc.getWorld()).getHandle(); ++ ++ player.spawnIn(serverLevel); ++ // Paper start - set raw so we aren't fully joined to the world (not added to chunk or world) ++ player.setPosRaw(loc.getX(), loc.getY(), loc.getZ()); ++ player.setRot(loc.getYaw(), loc.getPitch()); ++ // Paper end - set raw so we aren't fully joined to the world ++ // Spigot end ++ // LOGGER.info( // CraftBukkit - Moved message to after join ++ // "{}[{}] logged in with entity id {} at ({}, {}, {})", ++ // player.getName().getString(), ++ // loggableAddress, ++ // player.getId(), ++ // player.getX(), ++ // player.getY(), ++ // player.getZ() ++ // ); + LevelData levelData = serverLevel.getLevelData(); + player.loadGameTypes(optional1.orElse(null)); + ServerGamePacketListenerImpl serverGamePacketListenerImpl = new ServerGamePacketListenerImpl(this.server, connection, player, cookie); +@@ -196,8 +_,8 @@ + levelData.isHardcore(), + this.server.levelKeys(), + this.getMaxPlayers(), +- this.viewDistance, +- this.simulationDistance, ++ serverLevel.spigotConfig.viewDistance,// Spigot - view distance ++ serverLevel.spigotConfig.simulationDistance, + _boolean1, + !_boolean, + _boolean2, +@@ -205,6 +_,7 @@ + this.server.enforceSecureProfile() + ) + ); ++ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + serverGamePacketListenerImpl.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); + serverGamePacketListenerImpl.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); + serverGamePacketListenerImpl.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); +@@ -224,26 +_,119 @@ + mutableComponent = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), string); + } + +- this.broadcastSystemMessage(mutableComponent.withStyle(ChatFormatting.YELLOW), false); ++ // CraftBukkit start ++ mutableComponent.withStyle(ChatFormatting.YELLOW); ++ Component joinMessage = mutableComponent; // Paper - Adventure + serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); + ServerStatus status = this.server.getStatus(); + if (status != null && !cookie.transferred()) { + player.sendServerStatus(status); + } + +- player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); ++ // player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below + this.players.add(player); ++ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot + this.playersByUUID.put(player.getUUID(), player); +- this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); +- this.sendLevelInfo(player, serverLevel); ++ // this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below ++ // Paper start - Fire PlayerJoinEvent when Player is actually ready; correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks ++ player.supressTrackerForLogin = true; + serverLevel.addNewPlayer(player); +- this.server.getCustomBossEvents().onPlayerConnect(player); +- this.sendActivePlayerEffects(player); ++ this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below serverLevel.addPlayerJoin(player); ++ // Paper end - Fire PlayerJoinEvent when Player is actually ready + optional1.ifPresent(valueInput -> { + player.loadAndSpawnEnderPearls(valueInput); + player.loadAndSpawnParentVehicle(valueInput); + }); ++ // CraftBukkit start ++ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity(); ++ ++ // Ensure that player inventory is populated with its viewer ++ player.containerMenu.transferTo(player.containerMenu, bukkitPlayer); ++ ++ org.bukkit.event.player.PlayerJoinEvent playerJoinEvent = new org.bukkit.event.player.PlayerJoinEvent(bukkitPlayer, io.papermc.paper.adventure.PaperAdventure.asAdventure(mutableComponent)); // Paper - Adventure ++ this.cserver.getPluginManager().callEvent(playerJoinEvent); ++ ++ if (!player.connection.isAcceptingMessages()) { ++ return; + } + -+ if (entityplayer1 == player || !bukkitPlayer.canSee(entityplayer1.getBukkitEntity())) { // Paper - Use single player info update packet on join; Don't include joining player -+ continue; ++ final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage(); ++ ++ if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure ++ joinMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(jm); // Paper - Adventure ++ this.server.getPlayerList().broadcastSystemMessage(joinMessage, false); // Paper - Adventure ++ } ++ // CraftBukkit end ++ ++ // CraftBukkit start - sendAll above replaced with this loop ++ ClientboundPlayerInfoUpdatePacket packet = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player)); // Paper - Add Listing API for Player ++ ++ final List onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1); // Paper - Use single player info update packet on join ++ for (int i = 0; i < this.players.size(); ++i) { ++ ServerPlayer entityplayer1 = (ServerPlayer) this.players.get(i); ++ ++ if (entityplayer1.getBukkitEntity().canSee(bukkitPlayer)) { ++ // Paper start - Add Listing API for Player ++ if (entityplayer1.getBukkitEntity().isListed(bukkitPlayer)) { ++ // Paper end - Add Listing API for Player ++ entityplayer1.connection.send(packet); ++ // Paper start - Add Listing API for Player ++ } else { ++ entityplayer1.connection.send(ClientboundPlayerInfoUpdatePacket.createSinglePlayerInitializing(player, false)); ++ } ++ // Paper end - Add Listing API for Player ++ } ++ ++ if (entityplayer1 == player || !bukkitPlayer.canSee(entityplayer1.getBukkitEntity())) { // Paper - Use single player info update packet on join; Don't include joining player ++ continue; ++ } ++ ++ onlinePlayers.add(entityplayer1); // Paper - Use single player info update packet on join ++ } ++ // Paper start - Use single player info update packet on join ++ if (!onlinePlayers.isEmpty()) { ++ player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player)); // Paper - Add Listing API for Player ++ } ++ // Paper end - Use single player info update packet on join ++ player.sentListPacket = true; ++ player.supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready ++ ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now ++ // CraftBukkit end ++ ++ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE ++ ++ this.sendLevelInfo(player, serverLevel); ++ ++ // CraftBukkit start - Only add if the player wasn't moved in the event ++ if (player.level() == serverLevel && !serverLevel.players().contains(player)) { ++ serverLevel.addNewPlayer(player); ++ this.server.getCustomBossEvents().onPlayerConnect(player); + } + -+ onlinePlayers.add(entityplayer1); // Paper - Use single player info update packet on join -+ } -+ // Paper start - Use single player info update packet on join -+ if (!onlinePlayers.isEmpty()) { -+ player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player)); // Paper - Add Listing API for Player -+ } -+ // Paper end - Use single player info update packet on join -+ player.sentListPacket = true; -+ player.supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready -+ ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now -+ // CraftBukkit end -+ -+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE -+ -+ this.sendLevelInfo(player, serverLevel); -+ -+ // CraftBukkit start - Only add if the player wasn't moved in the event -+ if (player.level() == serverLevel && !serverLevel.players().contains(player)) { -+ serverLevel.addNewPlayer(player); -+ this.server.getCustomBossEvents().onPlayerConnect(player); -+ } -+ -+ serverLevel = player.serverLevel(); // CraftBukkit - Update in case join event changed it -+ // CraftBukkit end -+ this.sendActivePlayerEffects(player); -+ // Paper - move loading pearls / parent vehicle up - player.initInventoryMenu(); -+ // CraftBukkit - Moved from above, added world -+ // Paper start - Configurable player collision; Add to collideRule team if needed -+ final net.minecraft.world.scores.Scoreboard scoreboard = this.getServer().getLevel(Level.OVERWORLD).getScoreboard(); -+ final PlayerTeam collideRuleTeam = scoreboard.getPlayerTeam(this.collideRuleTeamName); -+ if (this.collideRuleTeamName != null && collideRuleTeam != null && player.getTeam() == null) { -+ scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); -+ } -+ // Paper end - Configurable player collision -+ PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), loggableAddress, player.getId(), serverLevel.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); -+ // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead -+ if (player.isDeadOrDying()) { -+ net.minecraft.core.Holder plains = serverLevel.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) ++ serverLevel = player.level(); // CraftBukkit - Update in case join event changed it ++ // CraftBukkit end ++ this.sendActivePlayerEffects(player); ++ // Paper - move loading pearls / parent vehicle up + player.initInventoryMenu(); ++ // CraftBukkit - Moved from above, added world ++ // Paper start - Configurable player collision; Add to collideRule team if needed ++ final net.minecraft.world.scores.Scoreboard scoreboard = this.getServer().getLevel(Level.OVERWORLD).getScoreboard(); ++ final PlayerTeam collideRuleTeam = scoreboard.getPlayerTeam(this.collideRuleTeamName); ++ if (this.collideRuleTeamName != null && collideRuleTeam != null && player.getTeam() == null) { ++ scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam); ++ } ++ // Paper end - Configurable player collision ++ PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), loggableAddress, player.getId(), serverLevel.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); ++ // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead ++ if (player.isDeadOrDying()) { ++ net.minecraft.core.Holder plains = serverLevel.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) + .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS); -+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( ++ player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( + new net.minecraft.world.level.chunk.EmptyLevelChunk(serverLevel, player.chunkPosition(), plains), + serverLevel.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null) -+ ); -+ } -+ // Paper end - Send empty chunk ++ ); ++ } ++ // Paper end - Send empty chunk + } } - public void updateEntireScoreboard(ServerScoreboard scoreboard, ServerPlayer player) { -@@ -261,30 +433,31 @@ public abstract class PlayerList { +@@ -267,30 +_,31 @@ } public void addWorldborderListener(ServerLevel level) { @@ -344,7 +341,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } @Override -@@ -312,56 +485,156 @@ public abstract class PlayerList { +@@ -319,56 +_,156 @@ } protected void save(ServerPlayer player) { @@ -370,7 +367,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + } + public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) { + // Paper end - Fix kick event leave message not being sent - ServerLevel serverLevel = player.serverLevel(); + ServerLevel serverLevel = player.level(); player.awardStat(Stats.LEAVE_GAME); + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it + // See SPIGOT-5799, SPIGOT-6145 @@ -431,7 +428,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + if (!thrownEnderpearl.level().paperConfig().misc.legacyEnderPearlBehavior) { + thrownEnderpearl.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, org.bukkit.event.entity.EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause + } else { -+ thrownEnderpearl.cachedOwner = null; ++ thrownEnderpearl.setOwner(null); + } + // Paper end - Allow using old ender pearl behavior } @@ -448,13 +445,15 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 this.playersByUUID.remove(uuid); - this.stats.remove(uuid); - this.advancements.remove(uuid); +- } +- +- this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID()))); + // CraftBukkit start + // this.stats.remove(uuid); + // this.advancements.remove(uuid); + // CraftBukkit end - } - -- this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID()))); ++ } ++ + // CraftBukkit start + // this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID()))); + ClientboundPlayerInfoRemovePacket packet = new ClientboundPlayerInfoRemovePacket(List.of(player.getUUID())); @@ -501,7 +500,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + // depending on the outcome. + SocketAddress socketAddress = loginlistener.connection.getRemoteAddress(); + -+ ServerPlayer entity = new ServerPlayer(this.server, this.server.getLevel(Level.OVERWORLD), gameProfile, ClientInformation.createDefault()); ++ ServerPlayer entity = new ServerPlayer(this.server, this.server.getLevel(Level.OVERWORLD), gameProfile, net.minecraft.server.level.ClientInformation.createDefault()); + entity.transferCookieConnection = loginlistener; + org.bukkit.entity.Player player = entity.getBukkitEntity(); + org.bukkit.event.player.PlayerLoginEvent event = new org.bukkit.event.player.PlayerLoginEvent(player, loginlistener.connection.hostname, ((java.net.InetSocketAddress) socketAddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.channel.remoteAddress()).getAddress()); @@ -513,7 +512,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned.reason", userBanListEntry.getReason()); if (userBanListEntry.getExpires() != null) { mutableComponent.append( -@@ -369,10 +642,12 @@ public abstract class PlayerList { +@@ -376,10 +_,12 @@ ); } @@ -530,7 +529,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 IpBanListEntry ipBanListEntry = this.ipBans.get(socketAddress); MutableComponent mutableComponent = Component.translatable("multiplayer.disconnect.banned_ip.reason", ipBanListEntry.getReason()); if (ipBanListEntry.getExpires() != null) { -@@ -381,69 +656,131 @@ public abstract class PlayerList { +@@ -388,65 +_,124 @@ ); } @@ -541,30 +540,9 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 - return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) - ? Component.translatable("multiplayer.disconnect.server_full") - : null; -+ // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) -+ // ? Component.translatable("multiplayer.disconnect.server_full") -+ // : null; -+ if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile)) { -+ event.disallow(org.bukkit.event.player.PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure -+ } -+ } -+ this.cserver.getPluginManager().callEvent(event); -+ if (event.getResult() != org.bukkit.event.player.PlayerLoginEvent.Result.ALLOWED) { -+ loginlistener.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.kickMessage())); // Paper - Adventure -+ return null; - } -+ return entity; - } - -- public ServerPlayer getPlayerForLogin(GameProfile gameProfile, ClientInformation clientInformation) { -- return new ServerPlayer(this.server, this.server.overworld(), gameProfile, clientInformation); -+ // CraftBukkit start - added EntityPlayer -+ public ServerPlayer getPlayerForLogin(GameProfile gameProfile, ClientInformation clientInformation, ServerPlayer player) { -+ player.updateOptions(clientInformation); -+ return player; -+ // CraftBukkit end - } - +- } +- } +- - public boolean disconnectAllPlayersWithProfile(GameProfile gameProfile) { - UUID id = gameProfile.getId(); - Set set = Sets.newIdentityHashSet(); @@ -572,8 +550,13 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 - for (ServerPlayer serverPlayer : this.players) { - if (serverPlayer.getUUID().equals(id)) { - set.add(serverPlayer); -- } -- } ++ // return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile) ++ // ? Component.translatable("multiplayer.disconnect.server_full") ++ // : null; ++ if (this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile)) { ++ event.disallow(org.bukkit.event.player.PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure + } + } - - ServerPlayer serverPlayer1 = this.playersByUUID.get(gameProfile.getId()); - if (serverPlayer1 != null) { @@ -583,6 +566,19 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 - for (ServerPlayer serverPlayer2 : set) { - serverPlayer2.connection.disconnect(DUPLICATE_LOGIN_DISCONNECT_MESSAGE); - } +- +- return !set.isEmpty(); +- } +- +- public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason) { ++ this.cserver.getPluginManager().callEvent(event); ++ if (event.getResult() != org.bukkit.event.player.PlayerLoginEvent.Result.ALLOWED) { ++ loginlistener.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.kickMessage())); // Paper - Adventure ++ return null; ++ } ++ return entity; ++ } ++ + public boolean disconnectAllPlayersWithProfile(GameProfile gameProfile, ServerPlayer player) { // CraftBukkit - added ServerPlayer + // CraftBukkit start - Moved up + // UUID id = gameProfile.getId(); @@ -607,19 +603,17 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + return player == null; + // CraftBukkit end + } - -- return !set.isEmpty(); ++ + // CraftBukkit start + public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, @Nullable org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason) { + return this.respawn(player, keepInventory, reason, eventReason, null); - } - -- public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason) { ++ } ++ + public ServerPlayer respawn(ServerPlayer player, boolean keepInventory, Entity.RemovalReason reason, @Nullable org.bukkit.event.player.PlayerRespawnEvent.RespawnReason eventReason, @Nullable org.bukkit.Location location) { + player.stopRiding(); // CraftBukkit this.players.remove(player); + this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot - player.serverLevel().removePlayerImmediately(player, reason); + player.level().removePlayerImmediately(player, reason); - TeleportTransition teleportTransition = player.findRespawnPositionAndUseSpawnBlock(!keepInventory, TeleportTransition.DO_NOTHING); - ServerLevel level = teleportTransition.newLevel(); - ServerPlayer serverPlayer = new ServerPlayer(this.server, level, player.getGameProfile(), player.clientInformation()); @@ -686,7 +680,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } byte b = (byte)(keepInventory ? 1 : 0); - ServerLevel serverLevel = serverPlayer.serverLevel(); + ServerLevel serverLevel = serverPlayer.level(); LevelData levelData = serverLevel.getLevelData(); serverPlayer.connection.send(new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(serverLevel), b)); - serverPlayer.connection.teleport(serverPlayer.getX(), serverPlayer.getY(), serverPlayer.getZ(), serverPlayer.getYRot(), serverPlayer.getXRot()); @@ -697,7 +691,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 serverPlayer.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle())); serverPlayer.connection.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); serverPlayer.connection -@@ -451,10 +788,13 @@ public abstract class PlayerList { +@@ -454,10 +_,13 @@ this.sendActivePlayerEffects(serverPlayer); this.sendLevelInfo(serverPlayer, level); this.sendPlayerPermissionLevel(serverPlayer); @@ -715,7 +709,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 serverPlayer.setHealth(serverPlayer.getHealth()); ServerPlayer.RespawnConfig respawnConfig = serverPlayer.getRespawnConfig(); if (!keepInventory && respawnConfig != null) { -@@ -477,8 +817,52 @@ public abstract class PlayerList { +@@ -480,8 +_,52 @@ ) ); } @@ -768,7 +762,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 return serverPlayer; } -@@ -488,24 +872,59 @@ public abstract class PlayerList { +@@ -491,24 +_,59 @@ } public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl connection) { @@ -831,7 +825,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 public void broadcastAll(Packet packet) { for (ServerPlayer serverPlayer : this.players) { serverPlayer.connection.send(packet); -@@ -581,6 +1000,12 @@ public abstract class PlayerList { +@@ -584,6 +_,12 @@ } private void sendPlayerPermissionLevel(ServerPlayer player, int permLevel) { @@ -844,7 +838,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 if (player.connection != null) { byte b; if (permLevel <= 0) { -@@ -594,11 +1019,33 @@ public abstract class PlayerList { +@@ -597,11 +_,33 @@ player.connection.send(new ClientboundEntityEventPacket(player, b)); } @@ -879,7 +873,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } public boolean isOp(GameProfile profile) { -@@ -609,21 +1056,17 @@ public abstract class PlayerList { +@@ -612,21 +_,17 @@ @Nullable public ServerPlayer getPlayerByName(String username) { @@ -907,7 +901,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 if (serverPlayer != except && serverPlayer.level().dimension() == dimension) { double d = x - serverPlayer.getX(); double d1 = y - serverPlayer.getY(); -@@ -636,9 +1079,11 @@ public abstract class PlayerList { +@@ -639,9 +_,11 @@ } public void saveAll() { @@ -919,7 +913,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } public UserWhiteList getWhiteList() { -@@ -661,14 +1106,18 @@ public abstract class PlayerList { +@@ -664,14 +_,18 @@ } public void sendLevelInfo(ServerPlayer player, ServerLevel level) { @@ -942,7 +936,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.LEVEL_CHUNKS_LOAD_START, 0.0F)); -@@ -677,8 +1126,21 @@ public abstract class PlayerList { +@@ -680,8 +_,21 @@ public void sendAllPlayerInfo(ServerPlayer player) { player.inventoryMenu.sendAllDataToRemote(); @@ -957,15 +951,15 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + player.refreshEntityData(player); // CraftBukkit - SPIGOT-7218: sync metadata player.connection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); + // CraftBukkit start - from GameRules -+ int i = player.serverLevel().getGameRules().getBoolean(GameRules.RULE_REDUCEDDEBUGINFO) ? 22 : 23; ++ int i = player.level().getGameRules().getBoolean(GameRules.RULE_REDUCEDDEBUGINFO) ? 22 : 23; + player.connection.send(new ClientboundEntityEventPacket(player, (byte) i)); -+ float immediateRespawn = player.serverLevel().getGameRules().getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN) ? 1.0F: 0.0F; ++ float immediateRespawn = player.level().getGameRules().getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN) ? 1.0F: 0.0F; + player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.IMMEDIATE_RESPAWN, immediateRespawn)); + // CraftBukkit end } public int getPlayerCount() { -@@ -694,6 +1156,7 @@ public abstract class PlayerList { +@@ -697,6 +_,7 @@ } public void setUsingWhiteList(boolean whitelistEnabled) { @@ -973,12 +967,14 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 this.doWhiteList = whitelistEnabled; } -@@ -731,11 +1194,36 @@ public abstract class PlayerList { +@@ -734,10 +_,35 @@ } public void removeAll() { - for (int i = 0; i < this.players.size(); i++) { - this.players.get(i).connection.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); +- } +- } + // Paper start - Extract method to allow for restarting flag + this.removeAll(false); + } @@ -989,7 +985,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + for (ServerPlayer player : this.players) { + if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); else // Paper - kick event cause (cause is never used here) + player.connection.disconnect(java.util.Objects.requireNonNullElseGet(this.server.server.shutdownMessage(), net.kyori.adventure.text.Component::empty)); // CraftBukkit - add custom shutdown message // Paper - Adventure - } ++ } + // CraftBukkit end + + // Paper start - Configurable player collision; Remove collideRule team if it exists @@ -999,8 +995,8 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + if (team != null) scoreboard.removePlayerTeam(team); + } + // Paper end - Configurable player collision - } - ++ } ++ + // CraftBukkit start + public void broadcastMessage(Component[] iChatBaseComponents) { + for (Component component : iChatBaseComponents) { @@ -1008,11 +1004,10 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 + } + } + // CraftBukkit end -+ + public void broadcastSystemMessage(Component message, boolean bypassHiddenChat) { this.broadcastSystemMessage(message, serverPlayer -> message, bypassHiddenChat); - } -@@ -756,20 +1244,39 @@ public abstract class PlayerList { +@@ -759,20 +_,39 @@ } public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound boundChatType) { @@ -1055,7 +1050,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 flag1 |= flag2 && message.isFullyFiltered(); } -@@ -782,14 +1289,21 @@ public abstract class PlayerList { +@@ -785,14 +_,21 @@ return message.hasSignature() && !message.hasExpiredServer(Instant.now()); } @@ -1081,7 +1076,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 Path path = file2.toPath(); if (FileUtil.isPathNormalized(path) && FileUtil.isPathPortable(path) && path.startsWith(file.getPath()) && file2.isFile()) { file2.renameTo(file1); -@@ -797,7 +1311,7 @@ public abstract class PlayerList { +@@ -800,7 +_,7 @@ } serverStatsCounter = new ServerStatsCounter(this.server, file1); @@ -1090,7 +1085,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } return serverStatsCounter; -@@ -805,11 +1319,11 @@ public abstract class PlayerList { +@@ -808,11 +_,11 @@ public PlayerAdvancements getPlayerAdvancements(ServerPlayer player) { UUID uuid = player.getUUID(); @@ -1104,7 +1099,7 @@ index aa7b0ea3f5203aafa86a8d36cd01b661cd16147c..ed23e8e29d08d69ec1d0dca49193a3a6 } playerAdvancements.setPlayer(player); -@@ -852,11 +1366,34 @@ public abstract class PlayerList { +@@ -855,11 +_,34 @@ } public void reloadResources() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch new file mode 100644 index 0000000000..21be4ecfcd --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java.patch @@ -0,0 +1,142 @@ +--- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java ++++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +@@ -79,6 +_,10 @@ + public final List entityInfoList = Lists.newArrayList(); + private Vec3i size = Vec3i.ZERO; + private String author = "?"; ++ // CraftBukkit start - data containers ++ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); ++ public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(StructureTemplate.DATA_TYPE_REGISTRY); ++ // CraftBukkit end + + public Vec3i getSize() { + return this.size; +@@ -255,6 +_,19 @@ + if (this.palettes.isEmpty()) { + return false; + } else { ++ // CraftBukkit start ++ // We only want the TransformerGeneratorAccess at certain locations because in here are many "block update" calls that shouldn't be transformed ++ ServerLevelAccessor wrappedAccess = serverLevel; ++ org.bukkit.craftbukkit.util.CraftStructureTransformer structureTransformer = null; ++ if (wrappedAccess instanceof org.bukkit.craftbukkit.util.TransformerGeneratorAccess transformerAccess) { ++ serverLevel = transformerAccess.getDelegate(); ++ structureTransformer = transformerAccess.getStructureTransformer(); ++ // The structureTransformer is not needed if we can not transform blocks therefore we can save a little bit of performance doing this ++ if (structureTransformer != null && !structureTransformer.canTransformBlocks()) { ++ structureTransformer = null; ++ } ++ } ++ // CraftBukkit end + List list = settings.getRandomPalette(this.palettes, offset).blocks(); + if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty()) + && this.size.getX() >= 1 +@@ -282,6 +_,21 @@ + serverLevel.setBlock(blockPos, Blocks.BARRIER.defaultBlockState(), 820); + } + ++ // CraftBukkit start ++ if (structureTransformer != null) { ++ org.bukkit.craftbukkit.block.CraftBlockState craftBlockState = (org.bukkit.craftbukkit.block.CraftBlockState) org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(serverLevel, blockPos, blockState, null); ++ if (structureBlockInfo.nbt != null && craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState entityState) { ++ entityState.loadData(structureBlockInfo.nbt); ++ if (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftLootable craftLootable) { ++ craftLootable.setSeed(random.nextLong()); ++ } ++ } ++ craftBlockState = structureTransformer.transformCraftState(craftBlockState); ++ blockState = craftBlockState.getHandle(); ++ structureBlockInfo = new StructureTemplate.StructureBlockInfo(blockPos, blockState, (craftBlockState instanceof org.bukkit.craftbukkit.block.CraftBlockEntityState craftBlockEntityState ? craftBlockEntityState.getSnapshotNBT() : null)); ++ } ++ // CraftBukkit end ++ + if (serverLevel.setBlock(blockPos, blockState, flags)) { + i = Math.min(i, blockPos.getX()); + i1 = Math.min(i1, blockPos.getY()); +@@ -293,7 +_,7 @@ + if (structureBlockInfo.nbt != null) { + BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos); + if (blockEntity != null) { +- if (blockEntity instanceof RandomizableContainer) { ++ if (structureTransformer == null && blockEntity instanceof RandomizableContainer) { // CraftBukkit - only process if don't have a transformer access (Was already set above) - SPIGOT-7520: Use structureTransformer as check, so that it is the same as above + structureBlockInfo.nbt.putLong("LootTableSeed", random.nextLong()); + } + +@@ -380,7 +_,11 @@ + if (pair1.getSecond() != null) { + BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos4); + if (blockEntity != null) { +- blockEntity.setChanged(); ++ // Paper start - Fix NBT pieces overriding a block entity during worldgen deadlock ++ if (!(serverLevel instanceof net.minecraft.world.level.WorldGenLevel)) { ++ blockEntity.setChanged(); ++ } ++ // Paper end - Fix NBT pieces overriding a block entity during worldgen deadlock + } + } + } +@@ -388,7 +_,7 @@ + + if (!settings.isIgnoreEntities()) { + this.placeEntities( +- serverLevel, ++ wrappedAccess, // CraftBukkit + offset, + settings.getMirror(), + settings.getRotation(), +@@ -499,14 +_,17 @@ + }); + } + } ++ + } + + private static Optional createEntityIgnoreException(ProblemReporter problemReporter, ServerLevelAccessor level, CompoundTag tag) { +- try { +- return EntityType.create(TagValueInput.create(problemReporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE); +- } catch (Exception var4) { +- return Optional.empty(); +- } ++ // CraftBukkit start ++ // try { ++ return EntityType.create(TagValueInput.create(problemReporter, level.registryAccess(), tag), level.getLevel(), EntitySpawnReason.STRUCTURE, true); // Paper - Don't fire sync event during generation ++ // } catch (Exception var4) { ++ // return Optional.empty(); ++ // } ++ // CraftBukkit end + } + + public Vec3i getSize(Rotation rotation) { +@@ -699,6 +_,11 @@ + + tag.put("entities", listTag3); + tag.put("size", this.newIntegerList(this.size.getX(), this.size.getY(), this.size.getZ())); ++ // CraftBukkit start - PDC ++ if (!this.persistentDataContainer.isEmpty()) { ++ tag.put("BukkitValues", this.persistentDataContainer.toTagCompound()); ++ } ++ // CraftBukkit end + return NbtUtils.addCurrentDataVersion(tag); + } + +@@ -729,6 +_,11 @@ + .ifPresent(compoundTag1 -> this.entityInfoList.add(new StructureTemplate.StructureEntityInfo(vec3, blockPos, compoundTag1))); + } + ); ++ // CraftBukkit start - PDC ++ if (tag.get("BukkitValues") instanceof CompoundTag compoundTag) { ++ this.persistentDataContainer.putAll(compoundTag); ++ } ++ // CraftBukkit end + } + + private void loadPalette(HolderGetter blockGetter, ListTag paletteTag, ListTag blocksTag) { +@@ -828,7 +_,7 @@ + + public static final class Palette { + private final List blocks; +- private final Map> cache = Maps.newHashMap(); ++ private final Map> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads + @Nullable + private List cachedJigsaws; +