PaperMC/patches/server/0245-Improve-death-events.patch
Nassim Jahnke 71c84c8132
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#10277)
Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
9a80d38c SPIGOT-336, SPIGOT-3366, SPIGOT-5768, SPIGOT-6409, SPIGOT-6861, PR-722: Add EntityRemoveEvent
258086d9 SPIGOT-7417, PR-967: Add Sign#getTargetSide and Sign#getAllowedEditor
ffaba051 SPIGOT-7584: Add missing Tag.ITEMS_NON_FLAMMABLE_WOOD

CraftBukkit Changes:
98b6c1ac7 SPIGOT-7589 Fix NullPointerException when bans expire
a2736ddb0 SPIGOT-336, SPIGOT-3366, SPIGOT-5768, SPIGOT-6409, SPIGOT-6861, PR-1008: Add EntityRemoveEvent
5bf12cb89 SPIGOT-7565: Throw a more descriptive error message when a developer tries to spawn an entity from a CraftBukkit class
76d95fe7e SPIGOT-7417, PR-1343: Add Sign#getTargetSide and Sign#getAllowedEditor

Spigot Changes:
e9ec5485 Rebuild patches
f1b62e0c Rebuild patches
2024-02-23 14:37:33 +01:00

525 lines
26 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Phoenix616 <mail@moep.tv>
Date: Tue, 21 Aug 2018 01:39:35 +0100
Subject: [PATCH] Improve death events
This adds the ability to cancel the death events and to modify the sound
an entity makes when dying. (In cases were no sound should it will be
called with shouldPlaySound set to false allowing unsilencing of silent
entities)
It makes handling of entity deaths a lot nicer as you no longer need
to listen on the damage event and calculate if the entity dies yourself
to cancel the death which has the benefit of also receiving the dropped
items and experience which is otherwise only properly possible by using
internal code.
== AT ==
public net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent;
public net.minecraft.world.entity.LivingEntity getSoundVolume()F
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 38a3b264ba72631c27203a178ac0bbdd36e10a10..80cedd5221d3c0dc475c631113e8782507067cb0 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -248,6 +248,10 @@ public class ServerPlayer extends Player {
private int containerCounter;
public boolean wonGame;
private int containerUpdateDelay; // Paper - Configurable container update tick rate
+ // Paper start - cancellable death event
+ public boolean queueHealthUpdatePacket;
+ public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
+ // Paper end - cancellable death event
// CraftBukkit start
public String displayName;
@@ -810,7 +814,7 @@ public class ServerPlayer extends Player {
@Override
public void die(DamageSource damageSource) {
- this.gameEvent(GameEvent.ENTITY_DIE);
+ // this.gameEvent(GameEvent.ENTITY_DIE); // Paper - move below event cancellation check
boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES);
// CraftBukkit start - fire PlayerDeathEvent
if (this.isRemoved()) {
@@ -838,6 +842,16 @@ public class ServerPlayer extends Player {
String deathmessage = defaultMessage.getString();
this.keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, PaperAdventure.asAdventure(defaultMessage), keepInventory); // Paper - Adventure
+ // Paper start - cancellable death event
+ if (event.isCancelled()) {
+ // make compatible with plugins that might have already set the health in an event listener
+ if (this.getHealth() <= 0) {
+ this.setHealth((float) event.getReviveHealth());
+ }
+ return;
+ }
+ this.gameEvent(GameEvent.ENTITY_DIE); // moved from the top of this method
+ // Paper end
// SPIGOT-943 - only call if they have an inventory open
if (this.containerMenu != this.inventoryMenu) {
@@ -986,8 +1000,17 @@ public class ServerPlayer extends Player {
}
}
}
-
- return super.hurt(source, amount);
+ // Paper start - cancellable death events
+ //return super.hurt(source, amount);
+ this.queueHealthUpdatePacket = true;
+ boolean damaged = super.hurt(source, amount);
+ this.queueHealthUpdatePacket = false;
+ if (this.queuedHealthUpdatePacket != null) {
+ this.connection.send(this.queuedHealthUpdatePacket);
+ this.queuedHealthUpdatePacket = null;
+ }
+ return damaged;
+ // Paper end
}
}
}
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index e8c4d42b1637752e69ea128fa7d0148bb5ffae06..39ff6dd7ee8e821f5693f6a6e01b713b25d30393 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -260,6 +260,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
public Set<UUID> collidableExemptions = new HashSet<>();
public boolean bukkitPickUpLoot;
public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper
+ public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event
@Override
public float getBukkitYaw() {
@@ -1530,13 +1531,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (this.isDeadOrDying()) {
if (!this.checkTotemDeathProtection(source)) {
- SoundEvent soundeffect = this.getDeathSound();
-
- if (flag1 && soundeffect != null) {
- this.playSound(soundeffect, this.getSoundVolume(), this.getVoicePitch());
- }
+ // Paper start - moved into CraftEventFactory event caller for cancellable death event
+ this.silentDeath = !flag1; // mark entity as dying silently
+ // Paper end
this.die(source);
+ this.silentDeath = false; // Paper - cancellable death event - reset to default
}
} else if (flag1) {
this.playHurtSound(source);
@@ -1688,6 +1688,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
Entity entity = damageSource.getEntity();
LivingEntity entityliving = this.getKillCredit();
+ /* // Paper - move down to make death event cancellable - this is the awardKillScore below
if (this.deathScore >= 0 && entityliving != null) {
entityliving.awardKillScore(this, this.deathScore, damageSource);
}
@@ -1699,24 +1700,59 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (!this.level().isClientSide && this.hasCustomName()) {
if (org.spigotmc.SpigotConfig.logNamedDeaths) LivingEntity.LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot
}
+ */ // Paper - move down to make death event cancellable - this is the awardKillScore below
this.dead = true;
- this.getCombatTracker().recheckStatus();
+ // Paper - moved into if below
Level world = this.level();
if (world instanceof ServerLevel) {
ServerLevel worldserver = (ServerLevel) world;
+ // Paper - move below into if for onKill
- if (entity == null || entity.killedEntity(worldserver, this)) {
+ // Paper start
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(damageSource);
+ if (deathEvent == null || !deathEvent.isCancelled()) {
+ if (this.deathScore >= 0 && entityliving != null) {
+ entityliving.awardKillScore(this, this.deathScore, damageSource);
+ }
+ // Paper start - clear equipment if event is not cancelled
+ if (this instanceof Mob) {
+ for (EquipmentSlot slot : this.clearedEquipmentSlots) {
+ this.setItemSlot(slot, ItemStack.EMPTY);
+ }
+ this.clearedEquipmentSlots.clear();
+ }
+ // Paper end
+
+ if (this.isSleeping()) {
+ this.stopSleeping();
+ }
+
+ if (!this.level().isClientSide && this.hasCustomName()) {
+ if (org.spigotmc.SpigotConfig.logNamedDeaths) LivingEntity.LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString()); // Spigot
+ }
+
+ this.getCombatTracker().recheckStatus();
+ if (entity != null) {
+ entity.killedEntity((ServerLevel) this.level(), this);
+ }
this.gameEvent(GameEvent.ENTITY_DIE);
- this.dropAllDeathLoot(damageSource);
- this.createWitherRose(entityliving);
+ } else {
+ this.dead = false;
+ this.setHealth((float) deathEvent.getReviveHealth());
}
- this.level().broadcastEntityEvent(this, (byte) 3);
+ // Paper end
+ this.createWitherRose(entityliving);
}
+ // Paper start
+ if (this.dead) { // Paper
+ this.level().broadcastEntityEvent(this, (byte) 3);
this.setPose(Pose.DYING);
+ }
+ // Paper end
}
}
@@ -1724,7 +1760,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (!this.level().isClientSide) {
boolean flag = false;
- if (adversary instanceof WitherBoss) {
+ if (this.dead && adversary instanceof WitherBoss) { // Paper
if (this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
BlockPos blockposition = this.blockPosition();
BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState();
@@ -1753,7 +1789,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
}
- protected void dropAllDeathLoot(DamageSource source) {
+ // Paper start
+ protected boolean clearEquipmentSlots = true;
+ protected Set<EquipmentSlot> clearedEquipmentSlots = new java.util.HashSet<>();
+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) {
+ // Paper end
Entity entity = source.getEntity();
int i;
@@ -1768,18 +1808,27 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.dropEquipment(); // CraftBukkit - from below
if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
this.dropFromLootTable(source, flag);
+ // Paper start
+ final boolean prev = this.clearEquipmentSlots;
+ this.clearEquipmentSlots = false;
+ this.clearedEquipmentSlots.clear();
+ // Paper end
this.dropCustomDeathLoot(source, i, flag);
+ this.clearEquipmentSlots = prev; // Paper
}
// CraftBukkit start - Call death event
- CraftEventFactory.callEntityDeathEvent(this, this.drops);
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops); // Paper
+ this.postDeathDropItems(deathEvent); // Paper
this.drops = new ArrayList<>();
// CraftBukkit end
// this.dropInventory();// CraftBukkit - moved up
this.dropExperience();
+ return deathEvent; // Paper
}
protected void dropEquipment() {}
+ protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled
// CraftBukkit start
public int getExpReward() {
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 655dfa9113ca82dc56bc8f4b083ea049ee5b4bee..3e6706671b3dc06b376eaa9d6d463c6a098104d1 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -1058,6 +1058,12 @@ public abstract class Mob extends LivingEntity implements Targeting {
}
+ // Paper start
+ protected boolean shouldSkipLoot(EquipmentSlot slot) { // method to avoid to fallback into the global mob loot logic (i.e fox)
+ return false;
+ }
+ // Paper end
+
@Override
protected void dropCustomDeathLoot(DamageSource source, int lootingMultiplier, boolean allowDrops) {
super.dropCustomDeathLoot(source, lootingMultiplier, allowDrops);
@@ -1066,6 +1072,7 @@ public abstract class Mob extends LivingEntity implements Targeting {
for (int k = 0; k < j; ++k) {
EquipmentSlot enumitemslot = aenumitemslot[k];
+ if (this.shouldSkipLoot(enumitemslot)) continue; // Paper
ItemStack itemstack = this.getItemBySlot(enumitemslot);
float f = this.getEquipmentDropChance(enumitemslot);
boolean flag1 = f > 1.0F;
@@ -1076,7 +1083,13 @@ public abstract class Mob extends LivingEntity implements Targeting {
}
this.spawnAtLocation(itemstack);
+ if (this.clearEquipmentSlots) { // Paper
this.setItemSlot(enumitemslot, ItemStack.EMPTY);
+ // Paper start
+ } else {
+ this.clearedEquipmentSlots.add(enumitemslot);
+ }
+ // Paper end
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
index 6376908df89af1eff3a948ca1faef5d4925f0c3b..4ac32078cb074a78a2bb83307013b849f8d59f23 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
@@ -715,16 +715,38 @@ public class Fox extends Animal implements VariantHolder<Fox.Type> {
return this.getTrustedUUIDs().contains(uuid);
}
+ // Paper start - handle the bitten item separately like vanilla
@Override
- protected void dropAllDeathLoot(DamageSource source) {
+ protected boolean shouldSkipLoot(EquipmentSlot slot) {
+ return slot == EquipmentSlot.MAINHAND;
+ }
+ // Paper end
+
+ @Override
+ // Paper start - Cancellable death event
+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(DamageSource source) {
ItemStack itemstack = this.getItemBySlot(EquipmentSlot.MAINHAND);
- if (!itemstack.isEmpty()) {
+ boolean releaseMouth = false;
+ if (!itemstack.isEmpty() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Fix MC-153010
this.spawnAtLocation(itemstack);
+ releaseMouth = true;
+ }
+
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = super.dropAllDeathLoot(source);
+
+ // Below is code to drop
+
+ if (deathEvent == null || deathEvent.isCancelled()) {
+ return deathEvent;
+ }
+
+ if (releaseMouth) {
+ // Paper end - Cancellable death event
this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
}
- super.dropAllDeathLoot(source);
+ return deathEvent; // Paper - Cancellable death event
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java
index bb399f775a5530a01f59332848c8ab9b8eceb2b5..14edfe103e61024b569f33de0b6608f39e749319 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractChestedHorse.java
@@ -70,11 +70,19 @@ public abstract class AbstractChestedHorse extends AbstractHorse {
this.spawnAtLocation(Blocks.CHEST);
}
- this.setChest(false);
+ //this.setCarryingChest(false); // Paper - moved to post death logic
}
}
+ // Paper start
+ protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {
+ if (this.hasChest() && (event == null || !event.isCancelled())) {
+ this.setChest(false);
+ }
+ }
+ // Paper end
+
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
index 0634b88a5bbfb549d5ec5931e19f674fb9ca4cbe..11555d1b771f93234098a3bc6a6eaacddeeeb4f6 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -493,8 +493,10 @@ public class ArmorStand extends LivingEntity {
}
// CraftBukkit end
if (source.is(DamageTypeTags.IS_EXPLOSION)) {
- this.brokenByAnything(source);
- this.kill();
+ // Paper start - avoid duplicate event call
+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(source);
+ if (!event.isCancelled()) this.kill(false);
+ // Paper end
return false;
} else if (source.is(DamageTypeTags.IGNITES_ARMOR_STANDS)) {
if (this.isOnFire()) {
@@ -537,9 +539,9 @@ public class ArmorStand extends LivingEntity {
this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity());
this.lastHit = i;
} else {
- this.brokenByPlayer(source);
+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByPlayer(source); // Paper
this.showBreakingParticles();
- this.discard(EntityRemoveEvent.Cause.DEATH); // CraftBukkit - SPIGOT-4890: remain as this.discard() since above damagesource method will call death event
+ if (!event.isCancelled()) this.kill(false); // Paper - we still need to kill to follow vanilla logic (emit the game event etc...)
}
return true;
@@ -591,8 +593,10 @@ public class ArmorStand extends LivingEntity {
f1 -= amount;
if (f1 <= 0.5F) {
- this.brokenByAnything(damageSource);
- this.kill();
+ // Paper start - avoid duplicate event call
+ org.bukkit.event.entity.EntityDeathEvent event = this.brokenByAnything(damageSource);
+ if (!event.isCancelled()) this.kill(false);
+ // Paper end
} else {
this.setHealth(f1);
this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity());
@@ -600,7 +604,7 @@ public class ArmorStand extends LivingEntity {
}
- private void brokenByPlayer(DamageSource damageSource) {
+ private org.bukkit.event.entity.EntityDeathEvent brokenByPlayer(DamageSource damageSource) { // Paper
ItemStack itemstack = new ItemStack(Items.ARMOR_STAND);
if (this.hasCustomName()) {
@@ -608,10 +612,10 @@ public class ArmorStand extends LivingEntity {
}
this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
- this.brokenByAnything(damageSource);
+ return this.brokenByAnything(damageSource); // Paper
}
- private void brokenByAnything(DamageSource damageSource) {
+ private org.bukkit.event.entity.EntityDeathEvent brokenByAnything(DamageSource damageSource) { // Paper
this.playBrokenSound();
// this.dropAllDeathLoot(damagesource); // CraftBukkit - moved down
@@ -633,7 +637,7 @@ public class ArmorStand extends LivingEntity {
this.armorItems.set(i, ItemStack.EMPTY);
}
}
- this.dropAllDeathLoot(damageSource); // CraftBukkit - moved from above
+ return this.dropAllDeathLoot(damageSource); // CraftBukkit - moved from above // Paper
}
@@ -760,7 +764,16 @@ public class ArmorStand extends LivingEntity {
@Override
public void kill() {
- org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, this.drops); // CraftBukkit - call event
+ // Paper start
+ kill(true);
+ }
+
+ public void kill(boolean callEvent) {
+ if (callEvent) {
+ // Paper end
+ org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, this.drops); // CraftBukkit - call event // Paper - make cancellable
+ if (event.isCancelled()) return; // Paper - make cancellable
+ } // Paper
this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
this.gameEvent(GameEvent.ENTITY_DIE);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 65c56d29e04fd0853a8bfe9f943d27fe71849cc2..317fb90ad77aaaddc6a84d719de94d52bba12508 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2434,7 +2434,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void sendHealthUpdate() {
FoodData foodData = this.getHandle().getFoodData();
- this.sendHealthUpdate(this.getScaledHealth(), foodData.getFoodLevel(), foodData.getSaturationLevel());
+ // Paper start - cancellable death event
+ ClientboundSetHealthPacket packet = new ClientboundSetHealthPacket(this.getScaledHealth(), foodData.getFoodLevel(), foodData.getSaturationLevel());
+ if (this.getHandle().queueHealthUpdatePacket) {
+ this.getHandle().queuedHealthUpdatePacket = packet;
+ } else {
+ this.getHandle().connection.send(packet);
+ }
+ // Paper end
}
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 4498bb89b219de0d1c62950b7aa5f17fe6eab7d0..542b9ff7d845dcebcf6569a6d4bd9e97ac3a436d 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -891,9 +891,16 @@ public class CraftEventFactory {
public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<org.bukkit.inventory.ItemStack> drops) {
CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity();
EntityDeathEvent event = new EntityDeathEvent(entity, drops, victim.getExpReward());
+ populateFields(victim, event); // Paper - make cancellable
CraftWorld world = (CraftWorld) entity.getWorld();
Bukkit.getServer().getPluginManager().callEvent(event);
+ // Paper start - make cancellable
+ if (event.isCancelled()) {
+ return event;
+ }
+ playDeathSound(victim, event);
+ // Paper end
victim.expToDrop = event.getDroppedExp();
for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
@@ -910,8 +917,15 @@ public class CraftEventFactory {
PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage);
event.setKeepInventory(keepInventory);
event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel
+ populateFields(victim, event); // Paper - make cancellable
org.bukkit.World world = entity.getWorld();
Bukkit.getServer().getPluginManager().callEvent(event);
+ // Paper start - make cancellable
+ if (event.isCancelled()) {
+ return event;
+ }
+ playDeathSound(victim, event);
+ // Paper end
victim.keepLevel = event.getKeepLevel();
victim.newLevel = event.getNewLevel();
@@ -928,6 +942,31 @@ public class CraftEventFactory {
return event;
}
+ // Paper start - helper methods for making death event cancellable
+ // Add information to death event
+ private static void populateFields(net.minecraft.world.entity.LivingEntity victim, EntityDeathEvent event) {
+ event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue());
+ event.setShouldPlayDeathSound(!victim.silentDeath && !victim.isSilent());
+ net.minecraft.sounds.SoundEvent soundEffect = victim.getDeathSound();
+ event.setDeathSound(soundEffect != null ? org.bukkit.craftbukkit.CraftSound.minecraftToBukkit(soundEffect) : null);
+ event.setDeathSoundCategory(org.bukkit.SoundCategory.valueOf(victim.getSoundSource().name()));
+ event.setDeathSoundVolume(victim.getSoundVolume());
+ event.setDeathSoundPitch(victim.getVoicePitch());
+ }
+
+ // Play death sound manually
+ private static void playDeathSound(net.minecraft.world.entity.LivingEntity victim, EntityDeathEvent event) {
+ if (event.shouldPlayDeathSound() && event.getDeathSound() != null && event.getDeathSoundCategory() != null) {
+ net.minecraft.world.entity.player.Player source = victim instanceof net.minecraft.world.entity.player.Player ? (net.minecraft.world.entity.player.Player) victim : null;
+ double x = event.getEntity().getLocation().getX();
+ double y = event.getEntity().getLocation().getY();
+ double z = event.getEntity().getLocation().getZ();
+ net.minecraft.sounds.SoundEvent soundEffect = org.bukkit.craftbukkit.CraftSound.bukkitToMinecraft(event.getDeathSound());
+ net.minecraft.sounds.SoundSource soundCategory = net.minecraft.sounds.SoundSource.valueOf(event.getDeathSoundCategory().name());
+ victim.level().playSound(source, x, y, z, soundEffect, soundCategory, event.getDeathSoundVolume(), event.getDeathSoundPitch());
+ }
+ }
+ // Paper end
/**
* Server methods
*/