aboutsummaryrefslogtreecommitdiffhomepage
path: root/Spigot-Server-Patches/0345-Improve-death-events.patch
diff options
context:
space:
mode:
Diffstat (limited to 'Spigot-Server-Patches/0345-Improve-death-events.patch')
-rw-r--r--Spigot-Server-Patches/0345-Improve-death-events.patch378
1 files changed, 378 insertions, 0 deletions
diff --git a/Spigot-Server-Patches/0345-Improve-death-events.patch b/Spigot-Server-Patches/0345-Improve-death-events.patch
new file mode 100644
index 0000000000..ddcd256635
--- /dev/null
+++ b/Spigot-Server-Patches/0345-Improve-death-events.patch
@@ -0,0 +1,378 @@
+From 31b199c08aabc3bac1268a37ab726e9905d67f3c Mon Sep 17 00:00:00 2001
+From: Phoenix616 <[email protected]>
+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.
+
+diff --git a/src/main/java/net/minecraft/server/CombatTracker.java b/src/main/java/net/minecraft/server/CombatTracker.java
+index bbd5e2b2a8..19750ceed1 100644
+--- a/src/main/java/net/minecraft/server/CombatTracker.java
++++ b/src/main/java/net/minecraft/server/CombatTracker.java
+@@ -175,6 +175,7 @@ public class CombatTracker {
+ this.h = null;
+ }
+
++ public void reset() { this.g(); } // Paper - OBFHELPER
+ public void g() {
+ int i = this.f ? 300 : 100;
+
+diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
+index 90e0d9d453..c4d4775627 100644
+--- a/src/main/java/net/minecraft/server/Entity.java
++++ b/src/main/java/net/minecraft/server/Entity.java
+@@ -1556,6 +1556,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
+ return false;
+ }
+
++ public void runKillTrigger(Entity entity, int kills, DamageSource damageSource) { this.a(entity, kills, damageSource); } // Paper - OBFHELPER
+ public void a(Entity entity, int i, DamageSource damagesource) {
+ if (entity instanceof EntityPlayer) {
+ CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource);
+@@ -2408,6 +2409,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
+ this.fallDistance = 0.0F;
+ }
+
++ public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER
+ public void b(EntityLiving entityliving) {}
+
+ protected boolean i(double d0, double d1, double d2) {
+@@ -3072,6 +3074,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
+ return EnumPistonReaction.NORMAL;
+ }
+
++ public SoundCategory getDeathSoundCategory() { return bV();} // Paper - OBFHELPER
+ public SoundCategory bV() {
+ return SoundCategory.NEUTRAL;
+ }
+diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java
+index 2c54e3e34a..a5cc5e2842 100644
+--- a/src/main/java/net/minecraft/server/EntityArmorStand.java
++++ b/src/main/java/net/minecraft/server/EntityArmorStand.java
+@@ -659,7 +659,8 @@ public class EntityArmorStand extends EntityLiving {
+ }
+
+ public void killEntity() {
+- org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, drops); // CraftBukkit - call event
++ org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, drops); // CraftBukkit - call event // Paper - make cancellable
++ if (event.isCancelled()) return; // Paper - make cancellable
+ this.die();
+ }
+
+diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
+index 6367fdd469..3d1cdd6271 100644
+--- a/src/main/java/net/minecraft/server/EntityLiving.java
++++ b/src/main/java/net/minecraft/server/EntityLiving.java
+@@ -76,14 +76,14 @@ public abstract class EntityLiving extends Entity {
+ public float aU;
+ public EntityHuman killer;
+ public int lastDamageByPlayerTime; // Paper - public
+- protected boolean killed;
++ protected boolean killed; protected void setDying(boolean dying) { this.killed = dying; } protected boolean isDying() { return this.killed; } // Paper - OBFHELPER
+ protected int ticksFarFromPlayer;
+ protected float aZ;
+ protected float ba;
+ protected float bb;
+ protected float bc;
+ protected float bd;
+- protected int be;
++ protected int be; protected int getKillCount() { return this.be; } // Paper - OBFHELPER
+ public float lastDamage;
+ protected boolean bg;
+ public float bh;
+@@ -124,6 +124,7 @@ public abstract class EntityLiving extends Entity {
+ public boolean collides = true;
+ public boolean canPickUpLoot;
+ 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() {
+@@ -1071,13 +1072,17 @@ public abstract class EntityLiving extends Entity {
+
+ if (this.getHealth() <= 0.0F) {
+ if (!this.e(damagesource)) {
+- SoundEffect soundeffect = this.cs();
++ // Paper start - moved into CraftEventFactory event caller for cancellable death event
++ //SoundEffect soundeffect = this.cs();
+
+- if (flag1 && soundeffect != null) {
+- this.a(soundeffect, this.cD(), this.cE());
+- }
++ //if (flag1 && soundeffect != null) {
++ // this.a(soundeffect, this.cD(), this.cE());
++ //}
++ this.silentDeath = !flag1; // mark entity as dying silently
++ // Paper end
+
+ this.die(damagesource);
++ this.silentDeath = false; // Paper - cancellable death event - reset to default
+ }
+ } else if (flag1) {
+ this.c(damagesource);
+@@ -1205,16 +1210,20 @@ public abstract class EntityLiving extends Entity {
+ Entity entity = damagesource.getEntity();
+ EntityLiving entityliving = this.cv();
+
+- if (this.be >= 0 && entityliving != null) {
+- entityliving.a(this, this.be, damagesource);
+- }
++ // Paper start - move down to make death event cancellable
++ //if (this.be >= 0 && entityliving != null) {
++ // entityliving.a(this, this.be, damagesource);
++ //}
+
+- if (entity != null) {
+- entity.b(this);
+- }
++ //if (entity != null) {
++ // entity.b(this);
++ //}
+
+ this.killed = true;
+- this.getCombatTracker().g();
++ //this.getCombatTracker().g();
++
++ org.bukkit.event.entity.EntityDeathEvent deathEvent = null;
++ // Paper end
+ if (!this.world.isClientSide) {
+ int i = 0;
+
+@@ -1227,15 +1236,33 @@ public abstract class EntityLiving extends Entity {
+
+ this.a(flag, i, damagesource);
+ // CraftBukkit start - Call death event
+- CraftEventFactory.callEntityDeathEvent(this, this.drops);
++ deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops); // Paper - cancellable death event
+ this.drops = new ArrayList<org.bukkit.inventory.ItemStack>();
+ } else {
+- CraftEventFactory.callEntityDeathEvent(this);
++ deathEvent = CraftEventFactory.callEntityDeathEvent(this); // Paper - cancellable death event
+ // CraftBukkit end
+ }
+ }
+
+- this.world.broadcastEntityEffect(this, (byte) 3);
++ // Paper start - cancellable death event
++ if (deathEvent == null || !deathEvent.isCancelled()) {
++ // triggers and stats got moved down
++ if (this.getKillCount() >= 0 && entityliving != null) {
++ entityliving.runKillTrigger(this, this.getKillCount(), damagesource);
++ }
++
++ if (entity != null) {
++ entity.onKill(this);
++ }
++
++ this.getCombatTracker().reset();
++ this.setDying(true);
++ this.world.broadcastEntityEffect(this, (byte) 3);
++ } else {
++ this.setDying(false); // Paper - reset if cancelled
++ this.setHealth((float) deathEvent.getReviveHealth());
++ }
++ // Paper end
+ }
+ }
+
+@@ -1289,6 +1316,7 @@ public abstract class EntityLiving extends Entity {
+ return SoundEffects.ENTITY_GENERIC_HURT;
+ }
+
++ @Nullable public SoundEffect getDeathSoundEffect() { return cs();} // Paper - OBFHELPER
+ @Nullable
+ protected SoundEffect cs() {
+ return SoundEffects.ENTITY_GENERIC_DEATH;
+@@ -1710,10 +1738,12 @@ public abstract class EntityLiving extends Entity {
+
+ }
+
++ public float getDeathSoundVolume() { return cD();} // Paper - OBFHELPER
+ protected float cD() {
+ return 1.0F;
+ }
+
++ public float getDeathSoundPitch() { return cE();} // Paper - OBFHELPER
+ protected float cE() {
+ return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.5F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F;
+ }
+diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
+index dc72538de6..62feadc8af 100644
+--- a/src/main/java/net/minecraft/server/EntityPlayer.java
++++ b/src/main/java/net/minecraft/server/EntityPlayer.java
+@@ -72,6 +72,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
+ public int ping;
+ public boolean viewingCredits;
+ private int containerUpdateDelay; // Paper
++ // Paper start - cancellable death event
++ public boolean queueHealthUpdatePacket = false;
++ public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
++ // Paper end
+
+ // CraftBukkit start
+ public String displayName;
+@@ -506,6 +510,15 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
+
+ String deathmessage = defaultMessage.getString();
+ org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
++ // 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;
++ }
++ // Paper end
+
+ // SPIGOT-943 - only call if they have an inventory open
+ if (this.activeContainer != this.defaultContainer) {
+@@ -638,8 +651,17 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
+ }
+ }
+ }
+-
+- return super.damageEntity(damagesource, f);
++ // Paper start - cancellable death events
++ //return super.damageEntity(damagesource, f);
++ this.queueHealthUpdatePacket = true;
++ boolean damaged = super.damageEntity(damagesource, f);
++ this.queueHealthUpdatePacket = false;
++ if (this.queuedHealthUpdatePacket != null) {
++ this.playerConnection.sendPacket(this.queuedHealthUpdatePacket);
++ this.queuedHealthUpdatePacket = null;
++ }
++ return damaged;
++ // Paper end
+ }
+ }
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftSound.java b/src/main/java/org/bukkit/craftbukkit/CraftSound.java
+index 17fab031b4..ee8219e3ba 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftSound.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftSound.java
+@@ -674,6 +674,22 @@ public enum CraftSound {
+ WEATHER_RAIN_ABOVE("weather.rain.above");
+ private final String minecraftKey;
+
++ // Paper start - cancellable death event
++ public static CraftSound getBySoundEffect(final SoundEffect effect) {
++ MinecraftKey key = IRegistry.SOUND_EVENT.getKey(effect);
++ Preconditions.checkArgument(key != null, "Key for sound effect %s not found?", effect.toString());
++
++ return valueOf(key.getKey().replace('.', '_').toUpperCase(java.util.Locale.ENGLISH));
++ }
++
++ public static Sound getSoundByEffect(final SoundEffect effect) {
++ return Sound.valueOf(getBySoundEffect(effect).name());
++ }
++
++ public static SoundEffect getSoundEffect(final Sound sound) {
++ return getSoundEffect(getSound(sound));
++ }
++ // Paper end
+ CraftSound(String minecraftKey) {
+ this.minecraftKey = minecraftKey;
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+index 7a918ea72f..8d32982c53 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+@@ -1691,7 +1691,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+ }
+
+ public void sendHealthUpdate() {
+- getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
++ // Paper start - cancellable death event
++ //getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
++ PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel());
++ if (this.getHandle().queueHealthUpdatePacket) {
++ this.getHandle().queuedHealthUpdatePacket = packet;
++ } else {
++ this.getHandle().playerConnection.sendPacket(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 e76862ef49..68e30185a2 100644
+--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+@@ -600,9 +600,16 @@ public class CraftEventFactory {
+ public static EntityDeathEvent callEntityDeathEvent(EntityLiving 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()) {
+@@ -618,8 +625,15 @@ public class CraftEventFactory {
+ CraftPlayer entity = victim.getBukkitEntity();
+ PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage);
+ event.setKeepInventory(keepInventory);
++ 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();
+@@ -640,6 +654,31 @@ public class CraftEventFactory {
+ return event;
+ }
+
++ // Paper start - helper methods for making death event cancellable
++ // Add information to death event
++ private static void populateFields(EntityLiving victim, EntityDeathEvent event) {
++ event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue());
++ event.setShouldPlayDeathSound(!victim.silentDeath && !victim.isSilent());
++ SoundEffect soundEffect = victim.getDeathSoundEffect();
++ event.setDeathSound(soundEffect != null ? org.bukkit.craftbukkit.CraftSound.getSoundByEffect(soundEffect) : null);
++ event.setDeathSoundCategory(org.bukkit.SoundCategory.valueOf(victim.getDeathSoundCategory().name()));
++ event.setDeathSoundVolume(victim.getDeathSoundVolume());
++ event.setDeathSoundPitch(victim.getDeathSoundPitch());
++ }
++
++ // Play death sound manually
++ private static void playDeathSound(EntityLiving victim, EntityDeathEvent event) {
++ if (event.shouldPlayDeathSound() && event.getDeathSound() != null && event.getDeathSoundCategory() != null) {
++ EntityHuman source = victim instanceof EntityHuman ? (EntityHuman) victim : null;
++ double x = event.getEntity().getLocation().getX();
++ double y = event.getEntity().getLocation().getY();
++ double z = event.getEntity().getLocation().getZ();
++ SoundEffect soundEffect = org.bukkit.craftbukkit.CraftSound.getSoundEffect(event.getDeathSound());
++ SoundCategory soundCategory = SoundCategory.valueOf(event.getDeathSoundCategory().name());
++ victim.world.sendSoundEffect(source, x, y, z, soundEffect, soundCategory, event.getDeathSoundVolume(), event.getDeathSoundPitch());
++ }
++ }
++ // Paper end
+ /**
+ * Server methods
+ */
+--
+2.21.0
+