Last per file patches

This commit is contained in:
Bjarne Koll
2025-05-31 14:44:19 +02:00
parent 4fe0ac240b
commit 19a36413c7
3 changed files with 451 additions and 464 deletions

View File

@@ -1,150 +0,0 @@
From 09671551669244ef4f259d8b27547e463d6795d4 Mon Sep 17 00:00:00 2001
From: File <noreply+automated@papermc.io>
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<StructureTemplate.StructureEntityInfo> 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<StructureTemplate.StructureBlockInfo> 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<Entity> 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<Block> blockGetter, ListTag paletteTag, ListTag blocksTag) {
@@ -817,7 +866,7 @@ public class StructureTemplate {
public static final class Palette {
private final List<StructureTemplate.StructureBlockInfo> blocks;
- private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newHashMap();
+ private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads
@Nullable
private List<StructureTemplate.JigsawBlockInfo> cachedJigsaws;

View File

@@ -1,14 +1,6 @@
From 09671551669244ef4f259d8b27547e463d6795d4 Mon Sep 17 00:00:00 2001
From: File <noreply+automated@papermc.io>
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<RegistryLayer> 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<CompoundTag> optional = this.load(player);
- ResourceKey<Level> resourceKey = optional.<ResourceKey<Level>>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<Level> 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<Long> worldUUIDMost = playerData.getLong("WorldUUIDMost");
+ final Optional<Long> worldUUIDLeast = playerData.getLong("WorldUUIDLeast");
+ final java.util.Optional<String> 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<ValueInput> optional1 = this.load(player, scopedCollector);
- ResourceKey<Level> resourceKey = optional1.<ResourceKey<Level>>flatMap(valueInput -> valueInput.read("Dimension", Level.RESOURCE_KEY_CODEC))
- .orElse(Level.OVERWORLD);
+ Optional<ValueInput> optional1 = this.load(player, scopedCollector); final Optional<ValueInput> 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.<ResourceKey<Level>>flatMap(
+ compoundTag -> {
+ com.mojang.serialization.DataResult<ResourceKey<Level>> dataResult = DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, compoundTag.get("Dimension")));
+ final Optional<ResourceKey<Level>> 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<ServerPlayer> 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<Level> 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<Long> worldUUIDMost = playerData.getLong("WorldUUIDMost");
+ final Optional<Long> worldUUIDLeast = playerData.getLong("WorldUUIDLeast");
+ final java.util.Optional<String> 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.<ResourceKey<Level>>flatMap(
+ compoundTag -> {
+ Optional<ResourceKey<Level>> 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<ServerPlayer> 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<net.minecraft.world.level.biome.Biome> 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<net.minecraft.world.level.biome.Biome> 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<ServerPlayer> 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() {

View File

@@ -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<StructureTemplate.StructureEntityInfo> 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<StructureTemplate.StructureBlockInfo> 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<Entity> 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<Block> blockGetter, ListTag paletteTag, ListTag blocksTag) {
@@ -828,7 +_,7 @@
public static final class Palette {
private final List<StructureTemplate.StructureBlockInfo> blocks;
- private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newHashMap();
+ private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads
@Nullable
private List<StructureTemplate.JigsawBlockInfo> cachedJigsaws;