From 4e30b91d4eabcf6738ed7881837968a4bdca6242 Mon Sep 17 00:00:00 2001 From: Max Lee Date: Sat, 8 Sep 2018 02:14:48 +0200 Subject: [PATCH] Improve death events (#1362) * Improve death events This adds the ability to cancel the events and to specify the sound. --- .../0130-Improve-death-events.patch | 177 ++++++++ Spigot-Server-Patches/0003-MC-Dev-fixes.patch | 24 +- .../0356-Improve-death-events.patch | 416 ++++++++++++++++++ scripts/importmcdev.sh | 4 + 4 files changed, 609 insertions(+), 12 deletions(-) create mode 100644 Spigot-API-Patches/0130-Improve-death-events.patch create mode 100644 Spigot-Server-Patches/0356-Improve-death-events.patch diff --git a/Spigot-API-Patches/0130-Improve-death-events.patch b/Spigot-API-Patches/0130-Improve-death-events.patch new file mode 100644 index 000000000..13994b151 --- /dev/null +++ b/Spigot-API-Patches/0130-Improve-death-events.patch @@ -0,0 +1,177 @@ +From b86fe574c68b575d7b24b8f893b3b472d6369fd0 Mon Sep 17 00:00:00 2001 +From: Phoenix616 +Date: Tue, 21 Aug 2018 01:32:28 +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/org/bukkit/event/entity/EntityDeathEvent.java b/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java +index ab9e81fd..a7b8f869 100644 +--- a/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java ++++ b/src/main/java/org/bukkit/event/entity/EntityDeathEvent.java +@@ -8,10 +8,19 @@ import org.bukkit.inventory.ItemStack; + /** + * Thrown whenever a LivingEntity dies + */ +-public class EntityDeathEvent extends EntityEvent { ++public class EntityDeathEvent extends EntityEvent implements org.bukkit.event.Cancellable { // Paper - make cancellable + private static final HandlerList handlers = new HandlerList(); + private final List drops; + private int dropExp = 0; ++ // Paper start - make cancellable ++ private boolean cancelled; ++ private double reviveHealth = 0; ++ private boolean shouldPlayDeathSound; ++ private org.bukkit.Sound deathSound; ++ private org.bukkit.SoundCategory deathSoundCategory; ++ private float deathSoundVolume; ++ private float deathSoundPitch; ++ // Paper end + + public EntityDeathEvent(final LivingEntity entity, final List drops) { + this(entity, drops, 0); +@@ -69,4 +78,132 @@ public class EntityDeathEvent extends EntityEvent { + public static HandlerList getHandlerList() { + return handlers; + } ++ ++ // Paper start - make cancellable ++ @Override ++ public boolean isCancelled() { ++ return cancelled; ++ } ++ ++ @Override ++ public void setCancelled(boolean cancel) { ++ cancelled = cancel; ++ } ++ ++ /** ++ * Get the amount of health that the entity should revive with after cancelling the event. ++ * Set to the entity's max health by default. ++ * ++ * @return The amount of health ++ */ ++ public double getReviveHealth() { ++ return reviveHealth; ++ } ++ ++ /** ++ * Set the amount of health that the entity should revive with after cancelling the event. ++ * Revive health value must be between 0 (exclusive) and the entity's max health (inclusive). ++ * ++ * @param reviveHealth The amount of health ++ * @throws IllegalArgumentException Thrown if the health is {@literal <= 0 or >} max health ++ */ ++ public void setReviveHealth(double reviveHealth) throws IllegalArgumentException { ++ double maxHealth = ((LivingEntity) entity).getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue(); ++ if ((reviveHealth <= 0) || (reviveHealth > maxHealth)) { ++ throw new IllegalArgumentException("Health must be between 0 (exclusive) and " + maxHealth + " (inclusive), but was " + reviveHealth); ++ } ++ this.reviveHealth = reviveHealth; ++ } ++ ++ ++ /** ++ * Whether or not the death sound should play when the entity dies. If the event is cancelled it does not play! ++ * ++ * @return Whether or not the death sound should play. Event is called with this set to false if the entity is silent. ++ */ ++ public boolean shouldPlayDeathSound() { ++ return shouldPlayDeathSound; ++ } ++ ++ /** ++ * Set whether or not the death sound should play when the entity dies. If the event is cancelled it does not play! ++ * ++ * @param playDeathSound Enable or disable the death sound ++ */ ++ public void setShouldPlayDeathSound(boolean playDeathSound) { ++ this.shouldPlayDeathSound = playDeathSound; ++ } ++ ++ /** ++ * Get the sound that the entity makes when dying ++ * ++ * @return The sound that the entity makes ++ */ ++ public org.bukkit.Sound getDeathSound() { ++ return deathSound; ++ } ++ ++ /** ++ * Set the sound that the entity makes when dying ++ * ++ * @param sound The sound that the entity should make when dying ++ */ ++ public void setDeathSound(org.bukkit.Sound sound) { ++ deathSound = sound; ++ } ++ ++ /** ++ * Get the sound category that the death sound should play in ++ * ++ * @return The sound category ++ */ ++ public org.bukkit.SoundCategory getDeathSoundCategory() { ++ return deathSoundCategory; ++ } ++ ++ /** ++ * Set the sound category that the death sound should play in. ++ * ++ * @param soundCategory The sound category ++ */ ++ public void setDeathSoundCategory(org.bukkit.SoundCategory soundCategory) { ++ this.deathSoundCategory = soundCategory; ++ } ++ ++ /** ++ * Get the volume that the death sound will play at. ++ * ++ * @return The volume the death sound will play at ++ */ ++ public float getDeathSoundVolume() { ++ return deathSoundVolume; ++ } ++ ++ /** ++ * Set the volume the death sound should play at. If the event is cancelled this will not play the sound! ++ * ++ * @param volume The volume the death sound should play at ++ */ ++ public void setDeathSoundVolume(float volume) { ++ this.deathSoundVolume = volume; ++ } ++ ++ /** ++ * Get the pitch that the death sound will play with. ++ * ++ * @return The pitch the death sound will play with ++ */ ++ public float getDeathSoundPitch() { ++ return deathSoundPitch; ++ } ++ ++ /** ++ * GSetet the pitch that the death sound should play with. ++ * ++ * @param pitch The pitch the death sound should play with ++ */ ++ public void setDeathSoundPitch(float pitch) { ++ this.deathSoundPitch = pitch; ++ } ++ // Paper end + } +-- +2.18.0.windows.1 + diff --git a/Spigot-Server-Patches/0003-MC-Dev-fixes.patch b/Spigot-Server-Patches/0003-MC-Dev-fixes.patch index 1599093c0..a14692b12 100644 --- a/Spigot-Server-Patches/0003-MC-Dev-fixes.patch +++ b/Spigot-Server-Patches/0003-MC-Dev-fixes.patch @@ -1,4 +1,4 @@ -From f4214b8d94c5a9690fa2e41f7b547545dec26549 Mon Sep 17 00:00:00 2001 +From 00d132db37f8d96814d5ce793d584937c0a0f896 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 30 Mar 2016 19:36:20 -0400 Subject: [PATCH] MC Dev fixes @@ -110,19 +110,19 @@ index a540167d6..b2860555d 100644 return this.a(jsonelement, type, jsondeserializationcontext); } } +diff --git a/src/main/java/net/minecraft/server/Registry.java b/src/main/java/net/minecraft/server/Registry.java +index 723372f26..c38c3768c 100644 +--- a/src/main/java/net/minecraft/server/Registry.java ++++ b/src/main/java/net/minecraft/server/Registry.java +@@ -1,3 +1,3 @@ + package net.minecraft.server; + +-public interface Registry extends Iterable {} ++public interface Registry extends Iterable {} // Paper - decompile fix diff --git a/src/main/java/net/minecraft/server/RegistryBlockID.java b/src/main/java/net/minecraft/server/RegistryBlockID.java -index 58f47d0de..8860a0129 100644 +index 58f47d0de..03894df54 100644 --- a/src/main/java/net/minecraft/server/RegistryBlockID.java +++ b/src/main/java/net/minecraft/server/RegistryBlockID.java -@@ -8,7 +8,7 @@ import java.util.Iterator; - import java.util.List; - import javax.annotation.Nullable; - --public class RegistryBlockID implements Registry { -+public class RegistryBlockID implements Registry { // Paper - Fix decompile error - - private final IdentityHashMap a; - private final List b; @@ -26,7 +26,7 @@ public class RegistryBlockID implements Registry { this.a.put(t0, Integer.valueOf(i)); @@ -252,5 +252,5 @@ index f5bcbdbe1..3190cadfc 100644 for (ZipEntry clazzEntry; (clazzEntry = nmsZipStream.getNextEntry()) != null; ) { final String entryName = clazzEntry.getName(); -- -2.17.1 +2.18.0.windows.1 diff --git a/Spigot-Server-Patches/0356-Improve-death-events.patch b/Spigot-Server-Patches/0356-Improve-death-events.patch new file mode 100644 index 000000000..736c0785a --- /dev/null +++ b/Spigot-Server-Patches/0356-Improve-death-events.patch @@ -0,0 +1,416 @@ +From b8c6e5d80cd3b21db5b3d9a031439d37143eb467 Mon Sep 17 00:00:00 2001 +From: Phoenix616 +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 7a076f3e4..bddd66e79 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 b0b49f4ff..d0dcce945 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -1471,6 +1471,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + 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); +@@ -2267,6 +2268,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + + } + ++ public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER + public void b(EntityLiving entityliving) {} + + protected boolean i(double d0, double d1, double d2) { +@@ -2965,6 +2967,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + return EnumPistonReaction.NORMAL; + } + ++ public SoundCategory getDeathSoundCategory() { return bK();} // Paper - OBFHELPER + public SoundCategory bK() { + return SoundCategory.NEUTRAL; + } +diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java +index dca497072..454c1e7d0 100644 +--- a/src/main/java/net/minecraft/server/EntityArmorStand.java ++++ b/src/main/java/net/minecraft/server/EntityArmorStand.java +@@ -641,7 +641,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 14637be49..dec4b442c 100644 +--- a/src/main/java/net/minecraft/server/EntityLiving.java ++++ b/src/main/java/net/minecraft/server/EntityLiving.java +@@ -75,14 +75,14 @@ public abstract class EntityLiving extends Entity { + public float aR; + public EntityHuman killer; + public int lastDamageByPlayerTime; // Paper - public +- protected boolean aU; ++ protected boolean aU; protected void setDying(boolean dying) { this.aU = dying; } protected boolean isDying() { return this.aU; } // Paper - OBFHELPER + protected int ticksFarFromPlayer; + protected float aW; + protected float aX; + protected float aY; + protected float aZ; + protected float ba; +- protected int bb; ++ protected int bb; protected int getKillCount() { return this.bb; } // Paper - OBFHELPER + public float lastDamage; + protected boolean bd; + public float be; +@@ -117,6 +117,7 @@ public abstract class EntityLiving extends Entity { + public org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; + public boolean collides = true; + public boolean canPickUpLoot; ++ public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event + + @Override + public float getBukkitYaw() { +@@ -970,13 +971,17 @@ public abstract class EntityLiving extends Entity { + + if (this.getHealth() <= 0.0F) { + if (!this.e(damagesource)) { +- SoundEffect soundeffect = this.cf(); ++ // Paper start - moved into CraftEventFactory event caller for cancellable death event ++ //SoundEffect soundeffect = this.cf(); + +- if (flag1 && soundeffect != null) { +- this.a(soundeffect, this.cq(), this.cr()); +- } ++ //if (flag1 && soundeffect != null) { ++ // this.a(soundeffect, this.cq(), this.cr()); ++ //} ++ 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); +@@ -1114,16 +1119,20 @@ public abstract class EntityLiving extends Entity { + Entity entity = damagesource.getEntity(); + EntityLiving entityliving = this.ci(); + +- if (this.bb >= 0 && entityliving != null) { +- entityliving.a(this, this.bb, damagesource); +- } ++ // Paper start - move down to make death event cancellable ++ //if (this.bb >= 0 && entityliving != null) { ++ // entityliving.a(this, this.bb, damagesource); ++ //} + +- if (entity != null) { +- entity.b(this); +- } ++ //if (entity != null) { ++ // entity.b(this); ++ //} + +- this.aU = true; +- this.getCombatTracker().g(); ++ //this.aU = true; ++ //this.getCombatTracker().g(); ++ ++ org.bukkit.event.entity.EntityDeathEvent deathEvent = null; ++ //Paper end + if (!this.world.isClientSide) { + int i = 0; + +@@ -1136,15 +1145,32 @@ 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(); + } 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.setHealth((float) deathEvent.getReviveHealth()); ++ } ++ // Paper end + } + } + +@@ -1198,6 +1224,7 @@ public abstract class EntityLiving extends Entity { + return SoundEffects.bX; + } + ++ @Nullable public SoundEffect getDeathSoundEffect() { return cf();} // Paper - OBFHELPER + @Nullable + protected SoundEffect cf() { + return SoundEffects.bS; +@@ -1583,10 +1610,12 @@ public abstract class EntityLiving extends Entity { + + } + ++ public float getDeathSoundVolume() { return cq();} // Paper - OBFHELPER + protected float cq() { + return 1.0F; + } + ++ public float getDeathSoundPitch() { return cr();} // Paper - OBFHELPER + protected float cr() { + 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 4ff505cfa..6afb6cf7b 100644 +--- a/src/main/java/net/minecraft/server/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/EntityPlayer.java +@@ -79,6 +79,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + } + // Paper end + 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; +@@ -436,9 +440,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + public void die(DamageSource damagesource) { + boolean flag = this.world.getGameRules().getBoolean("showDeathMessages"); + +- this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); ++ //this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event + // CraftBukkit start - fire PlayerDeathEvent + if (this.dead) { ++ this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event + return; + } + java.util.List loot = new java.util.ArrayList(this.inventory.getSize()); +@@ -456,6 +461,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + + String deathmessage = chatmessage.toPlainText(); + 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; ++ } ++ this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); ++ // Paper end + + String deathMessage = event.getDeathMessage(); + +@@ -608,8 +623,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/net/minecraft/server/RegistryMaterials.java b/src/main/java/net/minecraft/server/RegistryMaterials.java +index d26abb419..aaedbc3b7 100644 +--- a/src/main/java/net/minecraft/server/RegistryMaterials.java ++++ b/src/main/java/net/minecraft/server/RegistryMaterials.java +@@ -29,6 +29,7 @@ public class RegistryMaterials extends RegistrySimple implements Reg + return super.get(k0); + } + ++ @Nullable public K getByValue(V value) { return this.b(value); } // Paper - OBFHELPER + @Nullable + public K b(V v0) { + return this.b.get(v0); +diff --git a/src/main/java/net/minecraft/server/SoundEffect.java b/src/main/java/net/minecraft/server/SoundEffect.java +index ec37e237f..8e0da7bd7 100644 +--- a/src/main/java/net/minecraft/server/SoundEffect.java ++++ b/src/main/java/net/minecraft/server/SoundEffect.java +@@ -2,7 +2,7 @@ package net.minecraft.server; + + public class SoundEffect { + +- public static final RegistryMaterials a = new RegistryMaterials(); ++ public static final RegistryMaterials a = new RegistryMaterials(); public static RegistryMaterials getRegistry() { return a; }// Paper - OBFHELPER + private final MinecraftKey b; + private static int c; + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftSound.java b/src/main/java/org/bukkit/craftbukkit/CraftSound.java +index 8871c6f3a..84f4cb91e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftSound.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftSound.java +@@ -560,6 +560,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 = SoundEffect.getRegistry().getByValue(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 5f480ac06..d59d86efc 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1612,7 +1612,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 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 cce4acc0b..f1a3ca950 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -392,9 +392,16 @@ public class CraftEventFactory { + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim, List 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()) { +@@ -410,8 +417,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(); +@@ -432,6 +446,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.18.0.windows.1 + diff --git a/scripts/importmcdev.sh b/scripts/importmcdev.sh index 9e1605718..bc3b21f90 100755 --- a/scripts/importmcdev.sh +++ b/scripts/importmcdev.sh @@ -56,6 +56,7 @@ import ChunkCoordIntPair import ChunkProviderFlat import ChunkProviderGenerate import ChunkProviderHell +import CombatTracker import CommandAbstract import CommandScoreboard import CommandWhitelist @@ -103,10 +104,13 @@ import PersistentScoreboard import PersistentVillage import PlayerConnectionUtils import RegionFile +import Registry import RegistryBlockID +import RegistryMaterials import RemoteControlListener import RecipeBookServer import ServerPing +import SoundEffect import StructureBoundingBox import StructurePiece import StructureStart