aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch')
-rw-r--r--patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch921
1 files changed, 921 insertions, 0 deletions
diff --git a/patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch b/patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch
new file mode 100644
index 0000000000..55799925f7
--- /dev/null
+++ b/patch-remap/mache-spigotflower-stripped/net/minecraft/world/entity/LivingEntity.java.patch
@@ -0,0 +1,921 @@
+--- a/net/minecraft/world/entity/LivingEntity.java
++++ b/net/minecraft/world/entity/LivingEntity.java
+@@ -119,6 +120,26 @@
+ import net.minecraft.world.scores.PlayerTeam;
+ import org.slf4j.Logger;
+
++// CraftBukkit start
++import java.util.ArrayList;
++import java.util.HashSet;
++import java.util.Set;
++import com.google.common.base.Function;
++import org.bukkit.Location;
++import org.bukkit.craftbukkit.attribute.CraftAttributeMap;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.craftbukkit.inventory.CraftItemStack;
++import org.bukkit.entity.Player;
++import org.bukkit.event.entity.ArrowBodyCountChangeEvent;
++import org.bukkit.event.entity.EntityDamageEvent;
++import org.bukkit.event.entity.EntityDamageEvent.DamageModifier;
++import org.bukkit.event.entity.EntityPotionEffectEvent;
++import org.bukkit.event.entity.EntityRegainHealthEvent;
++import org.bukkit.event.entity.EntityResurrectEvent;
++import org.bukkit.event.entity.EntityTeleportEvent;
++import org.bukkit.event.player.PlayerItemConsumeEvent;
++// CraftBukkit end
++
+ public abstract class LivingEntity extends Entity implements Attackable {
+
+ private static final Logger LOGGER = LogUtils.getLogger();
+@@ -226,9 +247,23 @@
+ private float swimAmountO;
+ protected Brain<?> brain;
+ private boolean skipDropExperience;
++ // CraftBukkit start
++ public int expToDrop;
++ public boolean forceDrops;
++ public ArrayList<org.bukkit.inventory.ItemStack> drops = new ArrayList<org.bukkit.inventory.ItemStack>();
++ public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
++ public boolean collides = true;
++ public Set<UUID> collidableExemptions = new HashSet<>();
++ public boolean bukkitPickUpLoot;
+
+- protected LivingEntity(EntityType<? extends LivingEntity> entitytype, Level level) {
+- super(entitytype, level);
++ @Override
++ public float getBukkitYaw() {
++ return getYHeadRot();
++ }
++ // CraftBukkit end
++
++ protected LivingEntity(EntityType<? extends LivingEntity> entityType, Level level) {
++ super(entityType, level);
+ this.lastHandItemStacks = NonNullList.withSize(2, ItemStack.EMPTY);
+ this.lastArmorItemStacks = NonNullList.withSize(4, ItemStack.EMPTY);
+ this.discardFriction = false;
+@@ -237,8 +272,10 @@
+ this.effectsDirty = true;
+ this.useItem = ItemStack.EMPTY;
+ this.lastClimbablePos = Optional.empty();
+- this.attributes = new AttributeMap(DefaultAttributes.getSupplier(entitytype));
+- this.setHealth(this.getMaxHealth());
++ this.attributes = new AttributeMap(DefaultAttributes.getSupplier(entityType));
++ this.craftAttributes = new CraftAttributeMap(attributes); // CraftBukkit
++ // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
++ this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
+ this.blocksBuilding = true;
+ this.rotA = (float) ((Math.random() + 1.0D) * 0.009999999776482582D);
+ this.reapplyPosition();
+@@ -320,7 +354,13 @@
+ double d7 = Math.min((double) (0.2F + f / 15.0F), 2.5D);
+ int i = (int) (150.0D * d7);
+
+- ((ServerLevel) this.level()).sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, blockstate), d1, d2, d3, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D);
++ // CraftBukkit start - visiblity api
++ if (this instanceof ServerPlayer) {
++ ((ServerLevel) this.level()).sendParticles((ServerPlayer) this, new BlockParticleOption(ParticleTypes.BLOCK, onGround), this.getX(), this.getY(), this.getZ(), i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, false);
++ } else {
++ ((ServerLevel) this.level()).sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, onGround), d1, d2, d3, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D);
++ }
++ // CraftBukkit end
+ }
+
+ super.checkFallDamage(d0, flag, blockstate, blockpos);
+@@ -677,15 +714,21 @@
+ return true;
+ }
+
+- public void onEquipItem(EquipmentSlot equipmentslot, ItemStack itemstack, ItemStack itemstack1) {
++ public void onEquipItem(EquipmentSlot slot, ItemStack oldItem, ItemStack newItem) {
++ // CraftBukkit start
++ onEquipItem(slot, oldItem, newItem, false);
++ }
++
++ public void onEquipItem(EquipmentSlot enumitemslot, ItemStack itemstack, ItemStack itemstack1, boolean silent) {
++ // CraftBukkit end
+ boolean flag = itemstack1.isEmpty() && itemstack.isEmpty();
+
+ if (!flag && !ItemStack.isSameItemSameTags(itemstack, itemstack1) && !this.firstTick) {
+ Equipable equipable = Equipable.get(itemstack1);
+
+ if (!this.level().isClientSide() && !this.isSpectator()) {
+- if (!this.isSilent() && equipable != null && equipable.getEquipmentSlot() == equipmentslot) {
+- this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), equipable.getEquipSound(), this.getSoundSource(), 1.0F, 1.0F);
++ if (!this.isSilent() && equipable != null && equipable.getEquipmentSlot() == enumitemslot && !silent) { // CraftBukkit
++ this.level().playSound((net.minecraft.world.entity.player.Player) null, this.getX(), this.getY(), this.getZ(), equipable.getEquipSound(), this.getSoundSource(), 1.0F, 1.0F);
+ }
+
+ if (this.doesEmitEquipEvent(equipmentslot)) {
+@@ -761,9 +801,16 @@
+ }
+ }
+
+- if (compoundtag.contains("Health", 99)) {
+- this.setHealth(compoundtag.getFloat("Health"));
++ // CraftBukkit start
++ if (compound.contains("Bukkit.MaxHealth")) {
++ Tag nbtbase = compound.get("Bukkit.MaxHealth");
++ if (nbtbase.getId() == 5) {
++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(((FloatTag) nbtbase).getAsDouble());
++ } else if (nbtbase.getId() == 3) {
++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(((IntTag) nbtbase).getAsDouble());
++ }
+ }
++ // CraftBukkit end
+
+ this.hurtTime = compoundtag.getShort("HurtTime");
+ this.deathTime = compoundtag.getShort("DeathTime");
+@@ -798,9 +849,32 @@
+
+ }
+
++ // CraftBukkit start
++ private boolean isTickingEffects = false;
++ private List<ProcessableEffect> effectsToProcess = Lists.newArrayList();
++
++ private static class ProcessableEffect {
++
++ private MobEffect type;
++ private MobEffectInstance effect;
++ private final EntityPotionEffectEvent.Cause cause;
++
++ private ProcessableEffect(MobEffectInstance effect, EntityPotionEffectEvent.Cause cause) {
++ this.effect = effect;
++ this.cause = cause;
++ }
++
++ private ProcessableEffect(MobEffect type, EntityPotionEffectEvent.Cause cause) {
++ this.type = type;
++ this.cause = cause;
++ }
++ }
++ // CraftBukkit end
++
+ protected void tickEffects() {
+ Iterator iterator = this.activeEffects.keySet().iterator();
+
++ isTickingEffects = true; // CraftBukkit
+ try {
+ while (iterator.hasNext()) {
+ MobEffect mobeffect = (MobEffect) iterator.next();
+@@ -810,6 +884,12 @@
+ this.onEffectUpdated(mobeffectinstance, true, (Entity) null);
+ })) {
+ if (!this.level().isClientSide) {
++ // CraftBukkit start
++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect, null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.EXPIRATION);
++ if (event.isCancelled()) {
++ continue;
++ }
++ // CraftBukkit end
+ iterator.remove();
+ this.onEffectRemoved(mobeffectinstance);
+ }
+@@ -820,6 +900,17 @@
+ } catch (ConcurrentModificationException concurrentmodificationexception) {
+ ;
+ }
++ // CraftBukkit start
++ isTickingEffects = false;
++ for (ProcessableEffect e : effectsToProcess) {
++ if (e.effect != null) {
++ addEffect(e.effect, e.cause);
++ } else {
++ removeEffect(e.type, e.cause);
++ }
++ }
++ effectsToProcess.clear();
++ // CraftBukkit end
+
+ if (this.effectsDirty) {
+ if (!this.level().isClientSide) {
+@@ -946,7 +1037,13 @@
+ this.entityData.set(LivingEntity.DATA_EFFECT_COLOR_ID, 0);
+ }
+
++ // CraftBukkit start
+ public boolean removeAllEffects() {
++ return removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
++ }
++
++ public boolean removeAllEffects(EntityPotionEffectEvent.Cause cause) {
++ // CraftBukkit end
+ if (this.level().isClientSide) {
+ return false;
+ } else {
+@@ -955,7 +1052,14 @@
+ boolean flag;
+
+ for (flag = false; iterator.hasNext(); flag = true) {
+- this.onEffectRemoved((MobEffectInstance) iterator.next());
++ // CraftBukkit start
++ MobEffectInstance effect = (MobEffectInstance) iterator.next();
++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED);
++ if (event.isCancelled()) {
++ continue;
++ }
++ this.onEffectRemoved(effect);
++ // CraftBukkit end
+ iterator.remove();
+ }
+
+@@ -984,19 +1088,49 @@
+ return this.addEffect(mobeffectinstance, (Entity) null);
+ }
+
+- public boolean addEffect(MobEffectInstance mobeffectinstance, @Nullable Entity entity) {
+- if (!this.canBeAffected(mobeffectinstance)) {
++ // CraftBukkit start
++ public boolean addEffect(MobEffectInstance mobeffect, EntityPotionEffectEvent.Cause cause) {
++ return this.addEffect(mobeffect, (Entity) null, cause);
++ }
++
++ public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity) {
++ return this.addEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
++ }
++
++ public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) {
++ if (isTickingEffects) {
++ effectsToProcess.add(new ProcessableEffect(mobeffect, cause));
++ return true;
++ }
++ // CraftBukkit end
++
++ if (!this.canBeAffected(mobeffect)) {
+ return false;
+ } else {
+ MobEffectInstance mobeffectinstance1 = (MobEffectInstance) this.activeEffects.get(mobeffectinstance.getEffect());
+ boolean flag = false;
+
+- if (mobeffectinstance1 == null) {
+- this.activeEffects.put(mobeffectinstance.getEffect(), mobeffectinstance);
+- this.onEffectAdded(mobeffectinstance, entity);
++ // CraftBukkit start
++ boolean override = false;
++ if (mobeffect1 != null) {
++ override = new MobEffectInstance(mobeffect1).update(mobeffect);
++ }
++
++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override);
++ if (event.isCancelled()) {
++ return false;
++ }
++ // CraftBukkit end
++
++ if (mobeffect1 == null) {
++ this.activeEffects.put(mobeffect.getEffect(), mobeffect);
++ this.onEffectAdded(mobeffect, entity);
+ flag = true;
+- } else if (mobeffectinstance1.update(mobeffectinstance)) {
+- this.onEffectUpdated(mobeffectinstance1, true, entity);
++ // CraftBukkit start
++ } else if (event.isOverride()) {
++ mobeffect1.update(mobeffect);
++ this.onEffectUpdated(mobeffect1, true, entity);
++ // CraftBukkit end
+ flag = true;
+ }
+
+@@ -1034,6 +1168,7 @@
+ return this.getMobType() == MobType.UNDEAD;
+ }
+
++ // CraftBukkit start
+ @Nullable
+ public MobEffectInstance removeEffectNoUpdate(@Nullable MobEffect mobeffect) {
+ return (MobEffectInstance) this.activeEffects.remove(mobeffect);
+@@ -1042,8 +1181,29 @@
+ public boolean removeEffect(MobEffect mobeffect) {
+ MobEffectInstance mobeffectinstance = this.removeEffectNoUpdate(mobeffect);
+
+- if (mobeffectinstance != null) {
+- this.onEffectRemoved(mobeffectinstance);
++ MobEffectInstance effect = this.activeEffects.get(mobeffectlist);
++ if (effect == null) {
++ return null;
++ }
++
++ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause);
++ if (event.isCancelled()) {
++ return null;
++ }
++
++ return (MobEffectInstance) this.activeEffects.remove(mobeffectlist);
++ }
++
++ public boolean removeEffect(MobEffect effect) {
++ return removeEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
++ }
++
++ public boolean removeEffect(MobEffect mobeffectlist, EntityPotionEffectEvent.Cause cause) {
++ MobEffectInstance mobeffect = this.c(mobeffectlist, cause);
++ // CraftBukkit end
++
++ if (mobeffect != null) {
++ this.onEffectRemoved(mobeffect);
+ return true;
+ } else {
+ return false;
+@@ -1138,21 +1298,56 @@
+
+ }
+
+- public void heal(float f) {
++ // CraftBukkit start - Delegate so we can handle providing a reason for health being regained
++ public void heal(float healAmount) {
++ heal(healAmount, EntityRegainHealthEvent.RegainReason.CUSTOM);
++ }
++
++ public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason) {
+ float f1 = this.getHealth();
+
+ if (f1 > 0.0F) {
+- this.setHealth(f1 + f);
++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason);
++ // Suppress during worldgen
++ if (this.valid) {
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++ }
++
++ if (!event.isCancelled()) {
++ this.setHealth((float) (this.getHealth() + event.getAmount()));
++ }
++ // CraftBukkit end
+ }
+
+ }
+
+ public float getHealth() {
++ // CraftBukkit start - Use unscaled health
++ if (this instanceof ServerPlayer) {
++ return (float) ((ServerPlayer) this).getBukkitEntity().getHealth();
++ }
++ // CraftBukkit end
+ return (Float) this.entityData.get(LivingEntity.DATA_HEALTH_ID);
+ }
+
+- public void setHealth(float f) {
+- this.entityData.set(LivingEntity.DATA_HEALTH_ID, Mth.clamp(f, 0.0F, this.getMaxHealth()));
++ public void setHealth(float health) {
++ // CraftBukkit start - Handle scaled health
++ if (this instanceof ServerPlayer) {
++ org.bukkit.craftbukkit.entity.CraftPlayer player = ((ServerPlayer) this).getBukkitEntity();
++ // Squeeze
++ if (health < 0.0F) {
++ player.setRealHealth(0.0D);
++ } else if (health > player.getMaxHealth()) {
++ player.setRealHealth(player.getMaxHealth());
++ } else {
++ player.setRealHealth(health);
++ }
++
++ player.updateScaledHealth(false);
++ return;
++ }
++ // CraftBukkit end
++ this.entityData.set(LivingEntity.DATA_HEALTH_ID, Mth.clamp(health, 0.0F, this.getMaxHealth()));
+ }
+
+ public boolean isDeadOrDying() {
+@@ -1166,7 +1360,7 @@
+ return false;
+ } else if (this.level().isClientSide) {
+ return false;
+- } else if (this.isDeadOrDying()) {
++ } else if (this.isRemoved() || this.dead || this.getHealth() <= 0.0F) { // CraftBukkit - Don't allow entities that got set to dead/killed elsewhere to get damaged and die
+ return false;
+ } else if (damagesource.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) {
+ return false;
+@@ -1180,12 +1374,13 @@
+ boolean flag = false;
+ float f2 = 0.0F;
+
+- if (f > 0.0F && this.isDamageSourceBlocked(damagesource)) {
+- this.hurtCurrentlyUsedShield(f);
+- f2 = f;
+- f = 0.0F;
+- if (!damagesource.is(DamageTypeTags.IS_PROJECTILE)) {
+- Entity entity = damagesource.getDirectEntity();
++ // CraftBukkit - Moved into damageEntity0(DamageSource, float)
++ if (false && amount > 0.0F && this.isDamageSourceBlocked(source)) {
++ this.hurtCurrentlyUsedShield(amount);
++ f2 = amount;
++ amount = 0.0F;
++ if (!source.is(DamageTypeTags.IS_PROJECTILE)) {
++ Entity entity = source.getDirectEntity();
+
+ if (entity instanceof LivingEntity) {
+ LivingEntity livingentity = (LivingEntity) entity;
+@@ -1204,25 +1399,35 @@
+ this.walkAnimation.setSpeed(1.5F);
+ boolean flag1 = true;
+
+- if ((float) this.invulnerableTime > 10.0F && !damagesource.is(DamageTypeTags.BYPASSES_COOLDOWN)) {
+- if (f <= this.lastHurt) {
++ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && !source.is(DamageTypeTags.BYPASSES_COOLDOWN)) { // CraftBukkit - restore use of maxNoDamageTicks
++ if (amount <= this.lastHurt) {
+ return false;
+ }
+
+- this.actuallyHurt(damagesource, f - this.lastHurt);
+- this.lastHurt = f;
++ // CraftBukkit start
++ if (!this.damageEntity0(source, amount - this.lastHurt)) {
++ return false;
++ }
++ // CraftBukkit end
++ this.lastHurt = amount;
+ flag1 = false;
+ } else {
+- this.lastHurt = f;
+- this.invulnerableTime = 20;
+- this.actuallyHurt(damagesource, f);
++ // CraftBukkit start
++ if (!this.damageEntity0(source, amount)) {
++ return false;
++ }
++ this.lastHurt = amount;
++ this.invulnerableTime = this.invulnerableDuration; // CraftBukkit - restore use of maxNoDamageTicks
++ // this.damageEntity0(damagesource, f);
++ // CraftBukkit end
+ this.hurtDuration = 10;
+ this.hurtTime = this.hurtDuration;
+ }
+
+- if (damagesource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+- this.hurtHelmet(damagesource, f);
+- f *= 0.75F;
++ // CraftBukkit - Moved into damageEntity0(DamageSource, float)
++ if (false && source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
++ this.hurtHelmet(source, amount);
++ amount *= 0.75F;
+ }
+
+ Entity entity1 = damagesource.getEntity();
+@@ -1338,13 +1543,17 @@
+ InteractionHand[] ainteractionhand = InteractionHand.values();
+ int i = ainteractionhand.length;
+
++ // CraftBukkit start
++ EnumHand hand = null;
++ ItemStack itemstack1 = ItemStack.EMPTY;
+ for (int j = 0; j < i; ++j) {
+ InteractionHand interactionhand = ainteractionhand[j];
+ ItemStack itemstack1 = this.getItemInHand(interactionhand);
+
+ if (itemstack1.is(Items.TOTEM_OF_UNDYING)) {
++ hand = enumhand; // CraftBukkit
+ itemstack = itemstack1.copy();
+- itemstack1.shrink(1);
++ // itemstack1.subtract(1); // CraftBukkit
+ break;
+ }
+ }
+@@ -1353,16 +1563,26 @@
+ if (this instanceof ServerPlayer) {
+ ServerPlayer serverplayer = (ServerPlayer) this;
+
+- serverplayer.awardStat(Stats.ITEM_USED.get(Items.TOTEM_OF_UNDYING));
+- CriteriaTriggers.USED_TOTEM.trigger(serverplayer, itemstack);
++ if (!event.isCancelled()) {
++ if (!itemstack1.isEmpty()) {
++ itemstack1.shrink(1);
++ }
++ if (itemstack != null && this instanceof ServerPlayer) {
++ // CraftBukkit end
++ ServerPlayer entityplayer = (ServerPlayer) this;
++
++ entityplayer.awardStat(Stats.ITEM_USED.get(Items.TOTEM_OF_UNDYING));
++ CriteriaTriggers.USED_TOTEM.trigger(entityplayer, itemstack);
+ this.gameEvent(GameEvent.ITEM_INTERACT_FINISH);
+ }
+
+ this.setHealth(1.0F);
+- this.removeAllEffects();
+- this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 900, 1));
+- this.addEffect(new MobEffectInstance(MobEffects.ABSORPTION, 100, 1));
+- this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, 800, 0));
++ // CraftBukkit start
++ this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM);
++ this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 900, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM);
++ this.addEffect(new MobEffectInstance(MobEffects.ABSORPTION, 100, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM);
++ this.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, 800, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM);
++ // CraftBukkit end
+ this.level().broadcastEntityEvent(this, (byte) 35);
+ }
+
+@@ -1472,16 +1692,24 @@
+ BlockPos blockpos = this.blockPosition();
+ BlockState blockstate = Blocks.WITHER_ROSE.defaultBlockState();
+
+- if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) {
+- this.level().setBlock(blockpos, blockstate, 3);
+- flag = true;
++ if (this.level().getBlockState(blockposition).isAir() && iblockdata.canSurvive(this.level(), blockposition)) {
++ // CraftBukkit start - call EntityBlockFormEvent for Wither Rose
++ flag = org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this.level(), blockposition, iblockdata, 3, this);
++ // CraftBukkit end
+ }
+ }
+
+ if (!flag) {
+ ItemEntity itementity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), new ItemStack(Items.WITHER_ROSE));
+
+- this.level().addFreshEntity(itementity);
++ // CraftBukkit start
++ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
++ CraftEventFactory.callEvent(event);
++ if (event.isCancelled()) {
++ return;
++ }
++ // CraftBukkit end
++ this.level().addFreshEntity(entityitem);
+ }
+ }
+
+@@ -1500,22 +1728,38 @@
+
+ boolean flag = this.lastHurtByPlayerTime > 0;
+
++ this.dropEquipment(); // CraftBukkit - from below
+ if (this.shouldDropLoot() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
+ this.dropFromLootTable(damagesource, flag);
+ this.dropCustomDeathLoot(damagesource, i, flag);
+ }
++ // CraftBukkit start - Call death event
++ CraftEventFactory.callEntityDeathEvent(this, this.drops);
++ this.drops = new ArrayList<>();
++ // CraftBukkit end
+
+- this.dropEquipment();
++ // this.dropInventory();// CraftBukkit - moved up
+ this.dropExperience();
+ }
+
+ protected void dropEquipment() {}
+
+- protected void dropExperience() {
++ // CraftBukkit start
++ public int getExpReward() {
+ if (this.level() instanceof ServerLevel && !this.wasExperienceConsumed() && (this.isAlwaysExperienceDropper() || this.lastHurtByPlayerTime > 0 && this.shouldDropExperience() && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT))) {
+ ExperienceOrb.award((ServerLevel) this.level(), this.position(), this.getExperienceReward());
+ }
++ }
++ // CraftBukkit end
+
++ protected void dropExperience() {
++ // CraftBukkit start - Update getExpReward() above if the removed if() changes!
++ if (true && !(this instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon)) { // CraftBukkit - SPIGOT-2420: Special case ender dragon will drop the xp over time
++ ExperienceOrb.award((ServerLevel) this.level(), this.position(), this.expToDrop);
++ this.expToDrop = 0;
++ }
++ // CraftBukkit end
++
+ }
+
+ protected void dropCustomDeathLoot(DamageSource damagesource, int i, boolean flag) {}
+@@ -1606,6 +1853,28 @@
+ return itemstack.getEatingSound();
+ }
+
++ // CraftBukkit start - Add delegate methods
++ public SoundEvent getHurtSound0(DamageSource damagesource) {
++ return getHurtSound(damagesource);
++ }
++
++ public SoundEvent getDeathSound0() {
++ return getDeathSound();
++ }
++
++ public SoundEvent getFallDamageSound0(int fallHeight) {
++ return getFallDamageSound(fallHeight);
++ }
++
++ public SoundEvent getDrinkingSound0(ItemStack itemstack) {
++ return getDrinkingSound(itemstack);
++ }
++
++ public SoundEvent getEatingSound0(ItemStack itemstack) {
++ return getEatingSound(itemstack);
++ }
++ // CraftBukkit end
++
+ public Optional<BlockPos> getLastClimbablePos() {
+ return this.lastClimbablePos;
+ }
+@@ -1654,9 +1921,14 @@
+ int i = this.calculateFallDamage(f, f1);
+
+ if (i > 0) {
++ // CraftBukkit start
++ if (!this.hurt(source, (float) i)) {
++ return true;
++ }
++ // CraftBukkit end
+ this.playSound(this.getFallDamageSound(i), 1.0F, 1.0F);
+ this.playBlockFallSound();
+- this.hurt(damagesource, (float) i);
++ // this.damageEntity(damagesource, (float) i); // CraftBukkit - moved up
+ return true;
+ } else {
+ return flag;
+@@ -1707,10 +1978,10 @@
+
+ protected void hurtCurrentlyUsedShield(float f) {}
+
+- protected float getDamageAfterArmorAbsorb(DamageSource damagesource, float f) {
+- if (!damagesource.is(DamageTypeTags.BYPASSES_ARMOR)) {
+- this.hurtArmor(damagesource, f);
+- f = CombatRules.getDamageAfterAbsorb(f, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS));
++ protected float getDamageAfterArmorAbsorb(DamageSource damageSource, float damageAmount) {
++ if (!damageSource.is(DamageTypeTags.BYPASSES_ARMOR)) {
++ // this.hurtArmor(damagesource, f); // CraftBukkit - Moved into damageEntity0(DamageSource, float)
++ damageAmount = CombatRules.getDamageAfterAbsorb(damageAmount, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS));
+ }
+
+ return f;
+@@ -1722,7 +1993,8 @@
+ } else {
+ int i;
+
+- if (this.hasEffect(MobEffects.DAMAGE_RESISTANCE) && !damagesource.is(DamageTypeTags.BYPASSES_RESISTANCE)) {
++ // CraftBukkit - Moved to damageEntity0(DamageSource, float)
++ if (false && this.hasEffect(MobEffects.DAMAGE_RESISTANCE) && !damageSource.is(DamageTypeTags.BYPASSES_RESISTANCE)) {
+ i = (this.getEffect(MobEffects.DAMAGE_RESISTANCE).getAmplifier() + 1) * 5;
+ int j = 25 - i;
+ float f1 = f * (float) j;
+@@ -1755,11 +2027,16 @@
+ }
+ }
+
+- protected void actuallyHurt(DamageSource damagesource, float f) {
+- if (!this.isInvulnerableTo(damagesource)) {
+- f = this.getDamageAfterArmorAbsorb(damagesource, f);
+- f = this.getDamageAfterMagicAbsorb(damagesource, f);
+- float f1 = f;
++ // CraftBukkit start
++ protected boolean damageEntity0(final DamageSource damagesource, float f) { // void -> boolean, add final
++ if (!this.isInvulnerableTo(damagesource)) {
++ final boolean human = this instanceof net.minecraft.world.entity.player.Player;
++ float originalDamage = f;
++ Function<Double, Double> hardHat = new Function<Double, Double>() {
++ @Override
++ public Double apply(Double f) {
++ if (damagesource.is(DamageTypeTags.DAMAGES_HELMET) && !LivingEntity.this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
++ return -(f - (f * 0.75F));
+
+ f = Math.max(f - this.getAbsorptionAmount(), 0.0F);
+ this.setAbsorptionAmount(this.getAbsorptionAmount() - (f1 - f));
+@@ -1775,13 +2156,47 @@
+ }
+ }
+
+- if (f != 0.0F) {
++ if (f > 0 || !human) {
++ if (human) {
++ // PAIL: Be sure to drag all this code from the EntityHuman subclass each update.
++ ((net.minecraft.world.entity.player.Player) this).causeFoodExhaustion(damagesource.getFoodExhaustion(), org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.DAMAGED); // CraftBukkit - EntityExhaustionEvent
++ if (f < 3.4028235E37F) {
++ ((net.minecraft.world.entity.player.Player) this).awardStat(Stats.DAMAGE_TAKEN, Math.round(f * 10.0F));
++ }
++ }
++ // CraftBukkit end
+ this.getCombatTracker().recordDamage(damagesource, f);
+ this.setHealth(this.getHealth() - f);
+- this.setAbsorptionAmount(this.getAbsorptionAmount() - f);
++ // CraftBukkit start
++ if (!human) {
++ this.setAbsorptionAmount(this.getAbsorptionAmount() - f);
++ }
+ this.gameEvent(GameEvent.ENTITY_DAMAGE);
++
++ return true;
++ } else {
++ // Duplicate triggers if blocking
++ if (event.getDamage(DamageModifier.BLOCKING) < 0) {
++ if (this instanceof ServerPlayer) {
++ CriteriaTriggers.ENTITY_HURT_PLAYER.trigger((ServerPlayer) this, damagesource, f, originalDamage, true);
++ f2 = (float) -event.getDamage(DamageModifier.BLOCKING);
++ if (f2 > 0.0F && f2 < 3.4028235E37F) {
++ ((ServerPlayer) this).awardStat(Stats.DAMAGE_BLOCKED_BY_SHIELD, Math.round(originalDamage * 10.0F));
++ }
++ }
++
++ if (damagesource.getEntity() instanceof ServerPlayer) {
++ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger((ServerPlayer) damagesource.getEntity(), this, damagesource, f, originalDamage, true);
++ }
++
++ return false;
++ } else {
++ return originalDamage > 0;
++ }
++ // CraftBukkit end
+ }
+ }
++ return false; // CraftBukkit
+ }
+
+ public CombatTracker getCombatTracker() {
+@@ -1805,10 +2220,20 @@
+ return (Integer) this.entityData.get(LivingEntity.DATA_ARROW_COUNT_ID);
+ }
+
+- public final void setArrowCount(int i) {
+- this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, i);
++ public final void setArrowCount(int count) {
++ // CraftBukkit start
++ setArrowCount(count, false);
+ }
+
++ public final void setArrowCount(int i, boolean flag) {
++ ArrowBodyCountChangeEvent event = CraftEventFactory.callArrowBodyCountChangeEvent(this, getArrowCount(), i, flag);
++ if (event.isCancelled()) {
++ return;
++ }
++ this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, event.getNewAmount());
++ }
++ // CraftBukkit end
++
+ public final int getStingerCount() {
+ return (Integer) this.entityData.get(LivingEntity.DATA_STINGER_COUNT_ID);
+ }
+@@ -2053,6 +2474,12 @@
+
+ public abstract ItemStack getItemBySlot(EquipmentSlot slot);
+
++ // CraftBukkit start
++ public void setItemSlot(EquipmentSlot enumitemslot, ItemStack itemstack, boolean silent) {
++ this.setItemSlot(enumitemslot, itemstack);
++ }
++ // CraftBukkit end
++
+ @Override
+ @Override
+ public abstract void setItemSlot(EquipmentSlot slot, ItemStack stack);
+@@ -2291,6 +2714,7 @@
+ }
+
+ if (this.onGround() && !this.level().isClientSide) {
++ if (getSharedFlag(7) && !CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) // CraftBukkit
+ this.setSharedFlag(7, false);
+ }
+ } else {
+@@ -2462,7 +2885,7 @@
+ }
+ }
+
+- this.detectEquipmentUpdates();
++ this.detectEquipmentUpdatesPublic(); // CraftBukkit
+ if (this.tickCount % 20 == 0) {
+ this.getCombatTracker().recheckStatus();
+ }
+@@ -2559,7 +2982,7 @@
+ this.refreshDirtyAttributes();
+ }
+
+- private void detectEquipmentUpdates() {
++ public void detectEquipmentUpdatesPublic() { // CraftBukkit
+ Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
+
+ if (map != null) {
+@@ -2861,6 +3284,7 @@
+ }
+
+ if (!this.level().isClientSide) {
++ if (flag != this.getSharedFlag(7) && !CraftEventFactory.callToggleGlideEvent(this, flag).isCancelled()) // CraftBukkit
+ this.setSharedFlag(7, flag);
+ }
+
+@@ -3062,16 +3475,22 @@
+ @Override
+ @Override
+ public boolean isPickable() {
+- return !this.isRemoved();
++ return !this.isRemoved() && this.collides; // CraftBukkit
+ }
+
+ @Override
+ @Override
+ public boolean isPushable() {
+- return this.isAlive() && !this.isSpectator() && !this.onClimbable();
++ return this.isAlive() && !this.isSpectator() && !this.onClimbable() && this.collides; // CraftBukkit
+ }
+
++ // CraftBukkit start - collidable API
+ @Override
++ public boolean canCollideWithBukkit(Entity entity) {
++ return isPushable() && this.collides != this.collidableExemptions.contains(entity.getUUID());
++ }
++ // CraftBukkit end
++
+ @Override
+ public float getYHeadRot() {
+ return this.yHeadRot;
+@@ -3271,8 +3684,27 @@
+ } else {
+ if (!this.useItem.isEmpty() && this.isUsingItem()) {
+ this.triggerItemUseEffects(this.useItem, 16);
+- ItemStack itemstack = this.useItem.finishUsingItem(this.level(), this);
++ // CraftBukkit start - fire PlayerItemConsumeEvent
++ ItemStack itemstack;
++ if (this instanceof ServerPlayer) {
++ org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.useItem);
++ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(enumhand);
++ PlayerItemConsumeEvent event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem, hand);
++ this.level().getCraftServer().getPluginManager().callEvent(event);
+
++ if (event.isCancelled()) {
++ // Update client
++ ((ServerPlayer) this).getBukkitEntity().updateInventory();
++ ((ServerPlayer) this).getBukkitEntity().updateScaledHealth();
++ return;
++ }
++
++ itemstack = (craftItem.equals(event.getItem())) ? this.useItem.finishUsingItem(this.level(), this) : CraftItemStack.asNMSCopy(event.getItem()).finishUsingItem(this.level(), this);
++ } else {
++ itemstack = this.useItem.finishUsingItem(this.level(), this);
++ }
++ // CraftBukkit end
++
+ if (itemstack != this.useItem) {
+ this.setItemInHand(interactionhand, itemstack);
+ }
+@@ -3349,7 +3780,13 @@
+ return this.fallFlyTicks;
+ }
+
+- public boolean randomTeleport(double d0, double d1, double d2, boolean flag) {
++ public boolean randomTeleport(double x, double d1, double y, boolean flag) {
++ // CraftBukkit start
++ return randomTeleport(x, d1, y, flag, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN).orElse(false);
++ }
++
++ public Optional<Boolean> randomTeleport(double d0, double d1, double d2, boolean flag, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
++ // CraftBukkit end
+ double d3 = this.getX();
+ double d4 = this.getY();
+ double d5 = this.getZ();
+@@ -3374,16 +3811,41 @@
+ }
+
+ if (flag2) {
+- this.teleportTo(d0, d6, d2);
+- if (level.noCollision((Entity) this) && !level.containsAnyLiquid(this.getBoundingBox())) {
++ // CraftBukkit start - Teleport event
++ // this.teleportTo(d0, d6, d2);
++
++ // first set position, to check if the place to teleport is valid
++ this.setPos(d0, d6, d2);
++ if (world.noCollision((Entity) this) && !world.containsAnyLiquid(this.getBoundingBox())) {
+ flag1 = true;
+ }
++ // now revert and call event if the teleport place is valid
++ this.setPos(d3, d4, d5);
++
++ if (flag1) {
++ if (!(this instanceof ServerPlayer)) {
++ EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.level().getWorld(), d3, d4, d5), new Location(this.level().getWorld(), d0, d6, d2));
++ this.level().getCraftServer().getPluginManager().callEvent(teleport);
++ if (!teleport.isCancelled()) {
++ Location to = teleport.getTo();
++ this.teleportTo(to.getX(), to.getY(), to.getZ());
++ } else {
++ return Optional.empty();
++ }
++ } else {
++ // player teleport event is called in the underlining code
++ if (((ServerPlayer) this).connection.teleport(d0, d6, d2, this.getYRot(), this.getXRot(), java.util.Collections.emptySet(), cause)) {
++ return Optional.empty();
++ }
++ }
++ }
++ // CraftBukkit end
+ }
+ }
+
+ if (!flag1) {
+- this.teleportTo(d3, d4, d5);
+- return false;
++ // this.enderTeleportTo(d3, d4, d5); // CraftBukkit - already set the location back
++ return Optional.of(false); // CraftBukkit
+ } else {
+ if (flag) {
+ level.broadcastEntityEvent(this, (byte) 46);
+@@ -3395,7 +3857,7 @@
+ pathfindermob.getNavigation().stop();
+ }
+
+- return true;
++ return Optional.of(true); // CraftBukkit
+ }
+ }
+
+@@ -3570,7 +4028,7 @@
+ Pair<MobEffectInstance, Float> pair = (Pair) iterator.next();
+
+ if (!level.isClientSide && pair.getFirst() != null && level.random.nextFloat() < (Float) pair.getSecond()) {
+- livingentity.addEffect(new MobEffectInstance((MobEffectInstance) pair.getFirst()));
++ livingEntity.addEffect(new MobEffectInstance((MobEffectInstance) pair.getFirst()), EntityPotionEffectEvent.Cause.FOOD); // CraftBukkit
+ }
+ }
+ }