mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-01 00:10:32 +01:00
bd38e0318a
Updated Upstream (Bukkit/CraftBukkit) 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: f02baa38 PR-988: Add World#getIntersectingChunks(BoundingBox) 9321d665 Move getItemInUse up to LivingEntity 819eef73 PR-959: Add access to current item's remaining ticks c4fdadb0 SPIGOT-7601: Add AbstractArrow#getItem be8261ca Add support for Java 22 26119676 PR-979: Add more translation keys 66753362 PR-985: Correct book maximum pages and characters per page documentation c8be92fa PR-980: Improve getArmorContents() documentation f1120ee2 PR-983: Expose riptide velocity to PlayerRiptideEvent CraftBukkit Changes: dfaa89bbe PR-1369: Add World#getIntersectingChunks(BoundingBox) 51bbab2b9 Move getItemInUse up to LivingEntity 668e09602 PR-1331: Add access to current item's remaining ticks a639406d1 SPIGOT-7601: Add AbstractArrow#getItem 0398930fc SPIGOT-7602: Allow opening in-world horse and related inventories ffd15611c SPIGOT-7608: Allow empty lists to morph to any PDT list 2188dcfa9 Add support for Java 22 45d6a609f SPIGOT-7604: Revert "SPIGOT-7365: DamageCause blocked by shield should trigger invulnerableTime" 06d915943 SPIGOT-7365: DamageCause blocked by shield should trigger invulnerableTime ca3bc3707 PR-1361: Add more translation keys 366c3ca80 SPIGOT-7600: EntityChangeBlockEvent is not fired for frog eggs 06d0f9ba8 SPIGOT-7593: Fix sapling growth physics / client-side updates 45c2608e4 PR-1366: Expose riptide velocity to PlayerRiptideEvent 29b6bb79b SPIGOT-7587: Remove fixes for now-resolved MC-142590 and MC-109346
526 lines
26 KiB
Diff
526 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 de01edaa23fb8493faf46c02abcb47989ac9a582..7dbfc8f0e3eb54e86fef6d9bc68e082ae8cd4422 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 5b2365fc9a9364f2df7e5078c9d88572c074e4e3..449dab3d6e16b2b0fcc78c9e1b7173c822be9c53 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
|
@@ -892,9 +892,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()) {
|
|
@@ -911,8 +918,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();
|
|
@@ -929,6 +943,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
|
|
*/
|