diff --git a/paper-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java index d49dffbc26..6316a2f1d2 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java @@ -19,7 +19,9 @@ public class PlayerDeathEvent extends EntityDeathEvent { private int newExp = 0; private int newLevel = 0; private int newTotalExp = 0; + private boolean showDeathMessages; private Component deathMessage; + private Component deathScreenMessageOverride = null; private boolean doExpDrop; private boolean keepLevel = false; private boolean keepInventory = false; @@ -27,27 +29,28 @@ public class PlayerDeathEvent extends EntityDeathEvent { private final List itemsToKeep = new ArrayList<>(); @ApiStatus.Internal - public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final @Nullable Component deathMessage) { - this(player, damageSource, drops, droppedExp, 0, deathMessage); + public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final @Nullable Component deathMessage, final boolean showDeathMessages) { + this(player, damageSource, drops, droppedExp, 0, deathMessage, showDeathMessages); } @ApiStatus.Internal - public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final @Nullable Component deathMessage) { - this(player, damageSource, drops, droppedExp, newExp, 0, 0, deathMessage); + public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final @Nullable Component deathMessage, final boolean showDeathMessages) { + this(player, damageSource, drops, droppedExp, newExp, 0, 0, deathMessage, showDeathMessages); } @ApiStatus.Internal - public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final @Nullable Component deathMessage) { - this(player, damageSource, drops, droppedExp, newExp, newTotalExp, newLevel, deathMessage, true); + public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final @Nullable Component deathMessage, final boolean showDeathMessages) { + this(player, damageSource, drops, droppedExp, newExp, newTotalExp, newLevel, deathMessage, showDeathMessages, true); } @ApiStatus.Internal - public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final @Nullable Component deathMessage, final boolean doExpDrop) { + public PlayerDeathEvent(final @NotNull Player player, final @NotNull DamageSource damageSource, final @NotNull List drops, final int droppedExp, final int newExp, final int newTotalExp, final int newLevel, final @Nullable Component deathMessage, final boolean showDeathMessages, final boolean doExpDrop) { super(player, damageSource, drops, droppedExp); this.newExp = newExp; this.newTotalExp = newTotalExp; this.newLevel = newLevel; this.deathMessage = deathMessage; + this.showDeathMessages = showDeathMessages; this.doExpDrop = doExpDrop; } @@ -76,6 +79,7 @@ public class PlayerDeathEvent extends EntityDeathEvent { this.newExp = newExp; this.newTotalExp = newTotalExp; this.newLevel = newLevel; + this.showDeathMessages = true; this.deathMessage = LegacyComponentSerializer.legacySection().deserializeOrNull(deathMessage); this.doExpDrop = doExpDrop; } @@ -86,6 +90,30 @@ public class PlayerDeathEvent extends EntityDeathEvent { return (Player) this.entity; } + /** + * Get whether the death message should be shown. + * By default, this is determined by {@link org.bukkit.GameRule#SHOW_DEATH_MESSAGES}. + * + * @return whether the death message should be shown + * @see #deathMessage() + * @see #deathScreenMessageOverride() + */ + public boolean getShowDeathMessages() { + return showDeathMessages; + } + + /** + * Set whether the death message should be shown. + * By default, this is determined by {@link org.bukkit.GameRule#SHOW_DEATH_MESSAGES}. + * + * @param displayDeathMessage whether the death message should be shown + * @see #deathMessage() + * @see #deathScreenMessageOverride() + */ + public void setShowDeathMessages(boolean displayDeathMessage) { + this.showDeathMessages = displayDeathMessage; + } + /** * Clarity method for getting the player. Not really needed except * for reasons of clarity. @@ -177,7 +205,7 @@ public class PlayerDeathEvent extends EntityDeathEvent { /** * Set the death message that will appear to everyone on the server. * - * @param deathMessage Message to appear to other players on the server. + * @param deathMessage message to appear to other players on the server. * @deprecated in favour of {@link #deathMessage(Component)} */ @Deprecated @@ -197,6 +225,32 @@ public class PlayerDeathEvent extends EntityDeathEvent { return LegacyComponentSerializer.legacySection().serializeOrNull(this.deathMessage); } + /** + * Overrides the death message that will appear on the death screen of the dying player. + * By default, this is null. + *

+ * If set to null, death screen message will be same as {@code deathMessage()}. + *

+ * If the message exceeds 256 characters it will be truncated. + * + * @param deathScreenMessageOverride Message to appear on the death screen to the dying player. + */ + public void deathScreenMessageOverride(@Nullable Component deathScreenMessageOverride) { + this.deathScreenMessageOverride = deathScreenMessageOverride; + } + + /** + * Get the death message override that will appear on the death screen of the dying player. + * By default, this is null. + *

+ * If set to null, death screen message will be same as {@code deathMessage()}. + *

+ * @return Message to appear on the death screen to the dying player. + */ + public @Nullable Component deathScreenMessageOverride() { + return this.deathScreenMessageOverride; + } + /** * @return should experience be dropped from this death */ diff --git a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch index 59c0dc5687..29e206088b 100644 --- a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch @@ -27565,10 +27565,10 @@ index 085040aa98704f2874bcd95b751b0a81dcdb15ad..cd72273468f596b640bd2d10d846fbe8 } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 8bbbd1d64df1f4f4aecdbb1d1d65e258af018ca9..9eff33c3f552da794370d48a6ef526eecd2b131c 100644 +index 1a04df3ba206e473ff6faadf79435fad2e00c324..5a60f2598560571e156612bf256c1c340d92a922 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -187,7 +187,7 @@ import net.minecraft.world.scores.Team; +@@ -186,7 +186,7 @@ import net.minecraft.world.scores.Team; import net.minecraft.world.scores.criteria.ObjectiveCriteria; import org.slf4j.Logger; @@ -27577,7 +27577,7 @@ index 8bbbd1d64df1f4f4aecdbb1d1d65e258af018ca9..9eff33c3f552da794370d48a6ef526ee private static final Logger LOGGER = LogUtils.getLogger(); private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32; private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10; -@@ -423,6 +423,36 @@ public class ServerPlayer extends Player { +@@ -422,6 +422,36 @@ public class ServerPlayer extends Player { public @Nullable String clientBrandName = null; // Paper - Brand support public @Nullable org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event @@ -28597,7 +28597,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896 + // Paper end - block counting } diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 609b52150aab93b0bed3b41632c19a00e372fc64..f6cb7754e865b9df3f2e204a7ea9b522fb01851b 100644 +index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080df422be7 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -140,7 +140,7 @@ import net.minecraft.world.scores.ScoreHolder; @@ -29576,7 +29576,7 @@ index b766b4281aecb3b96e2c263664d81da3425e3653..c3bcb494afe464207e805f8c40b03c70 this(setDirty, true, ImmutableList.of()); } diff --git a/net/minecraft/world/entity/decoration/ArmorStand.java b/net/minecraft/world/entity/decoration/ArmorStand.java -index 75bf15ccd8a12153951f886ed87be9f3bece3133..6f601a0a300bbf01f77d835576d15e25c8ba10b8 100644 +index f5ce8151bb1bae9be638ced7f74899d452d517e1..5248f3c22abb608d7d7b338f169f13bfbf4cd2d6 100644 --- a/net/minecraft/world/entity/decoration/ArmorStand.java +++ b/net/minecraft/world/entity/decoration/ArmorStand.java @@ -245,7 +245,7 @@ public class ArmorStand extends LivingEntity { diff --git a/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch b/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch index e4f5f84b7f..1c04f6cb5a 100644 --- a/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch +++ b/paper-server/patches/features/0023-Incremental-chunk-and-player-saving.patch @@ -83,10 +83,10 @@ index 32c8d4675de341d5edad7dbd9c0bf4bce5037733..3c8a1fe9831d6cf9e622e3ac2aede4e5 // Paper start - add close param this.save(progress, flush, skipSave, false); diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 9eff33c3f552da794370d48a6ef526eecd2b131c..222bfe0a04b5d583852ea2c167d52d0e907cf006 100644 +index 5a60f2598560571e156612bf256c1c340d92a922..57e7d0a8b5f2a5bc65b0f290fb655625b1481f31 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -189,6 +189,7 @@ import org.slf4j.Logger; +@@ -188,6 +188,7 @@ import org.slf4j.Logger; public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system private static final Logger LOGGER = LogUtils.getLogger(); diff --git a/paper-server/patches/features/0025-Optional-per-player-mob-spawns.patch b/paper-server/patches/features/0025-Optional-per-player-mob-spawns.patch index 7cbbacf8b4..4b791809ab 100644 --- a/paper-server/patches/features/0025-Optional-per-player-mob-spawns.patch +++ b/paper-server/patches/features/0025-Optional-per-player-mob-spawns.patch @@ -78,10 +78,10 @@ index 5d63bf024cbcbd2f627c64fee77553c9a512bd15..f863377a807b672f49f7140688f378ec profiler.popPush("tickSpawningChunks"); diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 8e7ee4dc951eb53ccf65ab71214a0b89bd932ba0..73a450e045eba5dbfc7a4e861e4c614c8f60d6b4 100644 +index 57e7d0a8b5f2a5bc65b0f290fb655625b1481f31..60a46174d9cd6adc2fd141c1e5e4a439ded0fc45 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -403,6 +403,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -402,6 +402,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc public boolean queueHealthUpdatePacket; public @Nullable net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket; // Paper end - cancellable death event diff --git a/paper-server/patches/features/0026-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/paper-server/patches/features/0026-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch index 46cd1a1a54..7000a4a5ce 100644 --- a/paper-server/patches/features/0026-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch +++ b/paper-server/patches/features/0026-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch @@ -60,10 +60,10 @@ index f863377a807b672f49f7140688f378eca2cf650b..59e8a5e1b35c81883c9b1ca00c6e55d7 spawnState = NaturalSpawner.createState(naturalSpawnChunkCount, this.level.getAllEntities(), this::getFullChunk, null, true); } else { diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 73a450e045eba5dbfc7a4e861e4c614c8f60d6b4..105d6b3a40067f9e8ae5bbd9f2872171f73b3d07 100644 +index 60a46174d9cd6adc2fd141c1e5e4a439ded0fc45..75fa38570d758a48303a3a6dfb881b616a50f81c 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -407,6 +407,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc +@@ -406,6 +406,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc public static final int MOBCATEGORY_TOTAL_ENUMS = net.minecraft.world.entity.MobCategory.values().length; public final int[] mobCounts = new int[MOBCATEGORY_TOTAL_ENUMS]; // Paper end - Optional per player mob spawns @@ -72,7 +72,7 @@ index 73a450e045eba5dbfc7a4e861e4c614c8f60d6b4..105d6b3a40067f9e8ae5bbd9f2872171 public org.bukkit.craftbukkit.entity.CraftPlayer.TransferCookieConnection transferCookieConnection; public String displayName; diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index 17b13baa3465530b11ff918c806c772eb5c39a2c..afd6da5c361e1dcf311a9afe8a7efe2faef2556a 100644 +index c710e08ab48075ce7854e56826adb8f0364b025b..14a2514a408a66a83f7b5fb43b4c4dc8f23fd5f4 100644 --- a/net/minecraft/world/level/NaturalSpawner.java +++ b/net/minecraft/world/level/NaturalSpawner.java @@ -279,6 +279,11 @@ public final class NaturalSpawner { diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index f4e3afb90e..076f8107a1 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -1,5 +1,13 @@ --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java +@@ -65,7 +_,6 @@ + import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket; + import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket; + import net.minecraft.network.protocol.game.ClientboundOpenBookPacket; +-import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket; + import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; + import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket; + import net.minecraft.network.protocol.game.ClientboundPlayerCombatEndPacket; @@ -235,7 +_,8 @@ private int levitationStartTime; private boolean disconnected; @@ -316,12 +324,19 @@ } float saturationLevel = this.foodData.getSaturationLevel(); -@@ -793,15 +_,84 @@ +@@ -793,15 +_,36 @@ } private void updateScoreForCriteria(ObjectiveCriteria criteria, int points) { - this.getScoreboard().forAllObjectives(criteria, this, score -> score.set(points)); - } +- +- @Override +- public void die(DamageSource cause) { +- this.gameEvent(GameEvent.ENTITY_DIE); +- boolean _boolean = this.serverLevel().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); +- if (_boolean) { +- Component deathMessage = this.getCombatTracker().getDeathMessage(); + this.level().getCraftServer().getScoreboardManager().forAllObjectives(criteria, this, score -> score.set(points)); // CraftBukkit - Use our scores instead + } + @@ -348,13 +363,24 @@ + return false; + } + // Paper end - PlayerDeathEvent#getItemsToKeep - - @Override - public void die(DamageSource cause) { -- this.gameEvent(GameEvent.ENTITY_DIE); -- boolean _boolean = this.serverLevel().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); -- if (_boolean) { -- Component deathMessage = this.getCombatTracker().getDeathMessage(); ++ // Paper start - Expand PlayerDeathEvent API ++ private void sendClientboundPlayerCombatKillPacket(boolean displayMessage, Component deathMessage) { ++ if (displayMessage && deathMessage != CommonComponents.EMPTY) { ++ // Paper - moved from below die(DamageSource) method + this.connection + .send( + new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage), +@@ -818,6 +_,65 @@ + } + ) + ); ++ } else { ++ this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), CommonComponents.EMPTY)); ++ } ++ } ++ // Paper end - Expand PlayerDeathEvent API ++ @Override ++ public void die(DamageSource cause) { + // this.gameEvent(GameEvent.ENTITY_DIE); // Paper - move below event cancellation check + boolean _boolean = this.serverLevel().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES); final boolean showDeathMessage = _boolean; // Paper - OBFHELPER + // CraftBukkit start - fire PlayerDeathEvent @@ -382,7 +408,7 @@ + + String deathmessage = defaultMessage.getString(); + this.keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel -+ org.bukkit.event.entity.PlayerDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerDeathEvent(this, cause, loot, io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultMessage), keepInventory); // Paper - Adventure ++ org.bukkit.event.entity.PlayerDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerDeathEvent(this, cause, loot, io.papermc.paper.adventure.PaperAdventure.asAdventure(defaultMessage), showDeathMessage, keepInventory); // Paper - Adventure; Expand PlayerDeathEvent API + // Paper start - cancellable death event + if (event.isCancelled()) { + // make compatible with plugins that might have already set the health in an event listener @@ -400,13 +426,25 @@ + } + + net.kyori.adventure.text.Component apiDeathMessage = event.deathMessage() != null ? event.deathMessage() : net.kyori.adventure.text.Component.empty(); // Paper - Adventure ++ Component deathScreenMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(event.deathScreenMessageOverride() != null ? event.deathScreenMessageOverride() : apiDeathMessage); // Paper - Expand PlayerDeathEvent API + -+ if (apiDeathMessage != null && apiDeathMessage != net.kyori.adventure.text.Component.empty() && showDeathMessage) { // Paper - Adventure // TODO: allow plugins to override? ++ if (apiDeathMessage != null && apiDeathMessage != net.kyori.adventure.text.Component.empty() && event.getShowDeathMessages()) { // Paper - Adventure; Expand PlayerDeathEvent API + Component deathMessage = io.papermc.paper.adventure.PaperAdventure.asVanilla(apiDeathMessage); // Paper - Adventure + - this.connection - .send( - new ClientboundPlayerCombatKillPacket(this.getId(), deathMessage), ++ // Paper - moved up to sendClientboundPlayerCombatKillPacket() ++ sendClientboundPlayerCombatKillPacket(event.getShowDeathMessages(), deathScreenMessage); // Paper - Expand PlayerDeathEvent + Team team = this.getTeam(); + if (team == null || team.getDeathMessageVisibility() == Team.Visibility.ALWAYS) { + this.server.getPlayerList().broadcastSystemMessage(deathMessage, false); +@@ -827,7 +_,7 @@ + this.server.getPlayerList().broadcastSystemToAllExceptTeam(this, deathMessage); + } + } else { +- this.connection.send(new ClientboundPlayerCombatKillPacket(this.getId(), CommonComponents.EMPTY)); ++ sendClientboundPlayerCombatKillPacket(event.getShowDeathMessages(), deathScreenMessage); // Paper - Expand PlayerDeathEvent + } + + this.removeEntitiesOnShoulder(); @@ -835,11 +_,35 @@ this.tellNeutralMobsThatIDied(); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index a7e3afe04f..6f86a505c9 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -878,10 +878,10 @@ public class CraftEventFactory { return event; } - public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, DamageSource damageSource, List drops, net.kyori.adventure.text.Component deathMessage, boolean keepInventory) { // Paper - Adventure & Restore vanilla drops behavior + public static PlayerDeathEvent callPlayerDeathEvent(ServerPlayer victim, DamageSource damageSource, List drops, net.kyori.adventure.text.Component deathMessage, boolean showDeathMessages, boolean keepInventory) { CraftPlayer entity = victim.getBukkitEntity(); CraftDamageSource bukkitDamageSource = new CraftDamageSource(damageSource); - PlayerDeathEvent event = new PlayerDeathEvent(entity, bukkitDamageSource, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(victim.serverLevel(), damageSource.getEntity()), 0, deathMessage); // Paper - Restore vanilla drops behavior + PlayerDeathEvent event = new PlayerDeathEvent(entity, bukkitDamageSource, new io.papermc.paper.util.TransformingRandomAccessList<>(drops, Entity.DefaultDrop::stack, FROM_FUNCTION), victim.getExpReward(victim.serverLevel(), damageSource.getEntity()), 0, deathMessage, showDeathMessages); event.setKeepInventory(keepInventory); event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel populateFields(victim, event); // Paper - make cancellable