PaperMC/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch
Jake Potrebic ddbfcd4403 Restore vanilla entity drops behavior
Instead of just tracking the itemstacks, this tracks with it, the
action to take with that itemstack to apply the correct logic
on dropping the item instead of generalizing it for all dropped
items like CB does.
2022-03-22 09:34:41 -07:00

1934 lines
91 KiB
Diff

--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -42,6 +42,8 @@
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.FloatTag;
+import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
@@ -94,7 +96,6 @@
import net.minecraft.world.entity.animal.Wolf;
import net.minecraft.world.entity.boss.wither.WitherBoss;
import net.minecraft.world.entity.item.ItemEntity;
-import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.AxeItem;
@@ -135,6 +136,30 @@
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import org.slf4j.Logger;
+
+// CraftBukkit start
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.LinkedList;
+import java.util.UUID;
+import net.minecraft.world.item.component.Consumable;
+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.EntityKnockbackEvent;
+import org.bukkit.event.entity.EntityPotionEffectEvent;
+import org.bukkit.event.entity.EntityRegainHealthEvent;
+import org.bukkit.event.entity.EntityRemoveEvent;
+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 {
@@ -174,7 +199,7 @@
public static final float DEFAULT_BABY_SCALE = 0.5F;
public static final String ATTRIBUTES_FIELD = "attributes";
public static final Predicate<LivingEntity> PLAYER_NOT_WEARING_DISGUISE_ITEM = (entityliving) -> {
- if (entityliving instanceof Player entityhuman) {
+ if (entityliving instanceof net.minecraft.world.entity.player.Player entityhuman) {
ItemStack itemstack = entityhuman.getItemBySlot(EquipmentSlot.HEAD);
return !itemstack.is(ItemTags.GAZE_DISGUISE_EQUIPMENT);
@@ -210,7 +235,7 @@
public float yHeadRotO;
public final ElytraAnimationState elytraAnimationState;
@Nullable
- public Player lastHurtByPlayer;
+ public net.minecraft.world.entity.player.Player lastHurtByPlayer;
public int lastHurtByPlayerTime;
protected boolean dead;
protected int noActionTime;
@@ -260,7 +285,30 @@
protected boolean skipDropExperience;
private final EnumMap<EquipmentSlot, Reference2ObjectMap<Enchantment, Set<EnchantmentLocationBasedEffect>>> activeLocationDependentEnchantments;
protected float appliedScale;
+ // CraftBukkit start
+ public int expToDrop;
+ public ArrayList<DefaultDrop> drops = new ArrayList<>(); // Paper - Restore vanilla drops behavior
+ public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
+ public boolean collides = true;
+ 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
+ public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
+ @Override
+ public float getBukkitYaw() {
+ return this.getYHeadRot();
+ }
+ // CraftBukkit end
+ // Spigot start
+ public void inactiveTick()
+ {
+ super.inactiveTick();
+ ++this.noActionTime; // Above all the floats
+ }
+ // Spigot end
+
protected LivingEntity(EntityType<? extends LivingEntity> type, Level world) {
super(type, world);
this.lastHandItemStacks = NonNullList.withSize(2, ItemStack.EMPTY);
@@ -276,7 +324,9 @@
this.activeLocationDependentEnchantments = new EnumMap(EquipmentSlot.class);
this.appliedScale = 1.0F;
this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
- this.setHealth(this.getMaxHealth());
+ this.craftAttributes = new CraftAttributeMap(this.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();
@@ -356,7 +406,13 @@
double d8 = Math.min((double) (0.2F + f / 15.0F), 2.5D);
int i = (int) (150.0D * d8);
- worldserver.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), d2, d3, d4, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D);
+ // CraftBukkit start - visiblity api
+ if (this instanceof ServerPlayer) {
+ worldserver.sendParticlesSource((ServerPlayer) this, new BlockParticleOption(ParticleTypes.BLOCK, state), false, false, d2, d3, d4, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D);
+ } else {
+ worldserver.sendParticles(new BlockParticleOption(ParticleTypes.BLOCK, state), d2, d3, d4, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D);
+ }
+ // CraftBukkit end
}
}
}
@@ -402,7 +458,7 @@
}
if (this.isAlive()) {
- boolean flag = this instanceof Player;
+ boolean flag = this instanceof net.minecraft.world.entity.player.Player;
Level world1 = this.level();
ServerLevel worldserver1;
double d0;
@@ -424,7 +480,7 @@
}
if (this.isEyeInFluid(FluidTags.WATER) && !this.level().getBlockState(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())).is(Blocks.BUBBLE_COLUMN)) {
- boolean flag1 = !this.canBreatheUnderwater() && !MobEffectUtil.hasWaterBreathing(this) && (!flag || !((Player) this).getAbilities().invulnerable);
+ boolean flag1 = !this.canBreatheUnderwater() && !MobEffectUtil.hasWaterBreathing(this) && (!flag || !((net.minecraft.world.entity.player.Player) this).getAbilities().invulnerable);
if (flag1) {
this.setAirSupply(this.decreaseAirSupply(this.getAirSupply()));
@@ -573,7 +629,7 @@
++this.deathTime;
if (this.deathTime >= 20 && !this.level().isClientSide() && !this.isRemoved()) {
this.level().broadcastEntityEvent(this, (byte) 60);
- this.remove(Entity.RemovalReason.KILLED);
+ this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
}
}
@@ -629,7 +685,7 @@
return this.lastHurtByMobTimestamp;
}
- public void setLastHurtByPlayer(@Nullable Player attacking) {
+ public void setLastHurtByPlayer(@Nullable net.minecraft.world.entity.player.Player attacking) {
this.lastHurtByPlayer = attacking;
this.lastHurtByPlayerTime = this.tickCount;
}
@@ -667,7 +723,7 @@
}
public boolean shouldDiscardFriction() {
- return this.discardFriction;
+ return !this.frictionState.toBooleanOrElse(!this.discardFriction); // Paper - Friction API
}
public void setDiscardFriction(boolean noDrag) {
@@ -679,17 +735,23 @@
}
public void onEquipItem(EquipmentSlot slot, ItemStack oldStack, ItemStack newStack) {
+ // CraftBukkit start
+ this.onEquipItem(slot, oldStack, newStack, false);
+ }
+
+ public void onEquipItem(EquipmentSlot enumitemslot, ItemStack itemstack, ItemStack itemstack1, boolean silent) {
+ // CraftBukkit end
if (!this.level().isClientSide() && !this.isSpectator()) {
- boolean flag = newStack.isEmpty() && oldStack.isEmpty();
+ boolean flag = itemstack1.isEmpty() && itemstack.isEmpty();
- if (!flag && !ItemStack.isSameItemSameComponents(oldStack, newStack) && !this.firstTick) {
- Equippable equippable = (Equippable) newStack.get(DataComponents.EQUIPPABLE);
+ if (!flag && !ItemStack.isSameItemSameComponents(itemstack, itemstack1) && !this.firstTick) {
+ Equippable equippable = (Equippable) itemstack1.get(DataComponents.EQUIPPABLE);
- if (!this.isSilent() && equippable != null && slot == equippable.slot()) {
- this.level().playSeededSound((Player) null, this.getX(), this.getY(), this.getZ(), equippable.equipSound(), this.getSoundSource(), 1.0F, 1.0F, this.random.nextLong());
+ if (!this.isSilent() && equippable != null && enumitemslot == equippable.slot() && !silent) { // CraftBukkit
+ this.level().playSeededSound((net.minecraft.world.entity.player.Player) null, this.getX(), this.getY(), this.getZ(), equippable.equipSound(), this.getSoundSource(), 1.0F, 1.0F, this.random.nextLong());
}
- if (this.doesEmitEquipEvent(slot)) {
+ if (this.doesEmitEquipEvent(enumitemslot)) {
this.gameEvent(equippable != null ? GameEvent.EQUIP : GameEvent.UNEQUIP);
}
@@ -699,17 +761,24 @@
@Override
public void remove(Entity.RemovalReason reason) {
- if (reason == Entity.RemovalReason.KILLED || reason == Entity.RemovalReason.DISCARDED) {
+ // CraftBukkit start - add Bukkit remove cause
+ this.remove(reason, null);
+ }
+
+ @Override
+ public void remove(Entity.RemovalReason entity_removalreason, EntityRemoveEvent.Cause cause) {
+ // CraftBukkit end
+ if (entity_removalreason == Entity.RemovalReason.KILLED || entity_removalreason == Entity.RemovalReason.DISCARDED) {
Level world = this.level();
if (world instanceof ServerLevel) {
ServerLevel worldserver = (ServerLevel) world;
- this.triggerOnDeathMobEffects(worldserver, reason);
+ this.triggerOnDeathMobEffects(worldserver, entity_removalreason);
}
}
- super.remove(reason);
+ super.remove(entity_removalreason, cause); // CraftBukkit
this.brain.clearMemories();
}
@@ -722,11 +791,17 @@
mobeffect.onMobRemoved(world, this, reason);
}
+ this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DEATH); // CraftBukkit
this.activeEffects.clear();
}
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
+ // Paper start - Friction API
+ if (this.frictionState != net.kyori.adventure.util.TriState.NOT_SET) {
+ nbt.putString("Paper.FrictionState", this.frictionState.toString());
+ }
+ // Paper end - Friction API
nbt.putFloat("Health", this.getHealth());
nbt.putShort("HurtTime", (short) this.hurtTime);
nbt.putInt("HurtByTimestamp", this.lastHurtByMobTimestamp);
@@ -763,7 +838,23 @@
@Override
public void readAdditionalSaveData(CompoundTag nbt) {
- this.internalSetAbsorptionAmount(nbt.getFloat("AbsorptionAmount"));
+ // Paper start - Check for NaN
+ float absorptionAmount = nbt.getFloat("AbsorptionAmount");
+ if (Float.isNaN(absorptionAmount)) {
+ absorptionAmount = 0;
+ }
+ this.internalSetAbsorptionAmount(absorptionAmount);
+ // Paper end - Check for NaN
+ // Paper start - Friction API
+ if (nbt.contains("Paper.FrictionState")) {
+ String fs = nbt.getString("Paper.FrictionState");
+ try {
+ frictionState = net.kyori.adventure.util.TriState.valueOf(fs);
+ } catch (Exception ignored) {
+ LOGGER.error("Unknown friction state " + fs + " for " + this);
+ }
+ }
+ // Paper end - Friction API
if (nbt.contains("attributes", 9) && this.level() != null && !this.level().isClientSide) {
this.getAttributes().load(nbt.getList("attributes", 10));
}
@@ -781,6 +872,17 @@
}
}
+ // CraftBukkit start
+ if (nbt.contains("Bukkit.MaxHealth")) {
+ Tag nbtbase = nbt.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
+
if (nbt.contains("Health", 99)) {
this.setHealth(nbt.getFloat("Health"));
}
@@ -792,6 +894,7 @@
String s = nbt.getString("Team");
Scoreboard scoreboard = this.level().getScoreboard();
PlayerTeam scoreboardteam = scoreboard.getPlayerTeam(s);
+ if (!this.level().paperConfig().scoreboards.allowNonPlayerEntitiesOnScoreboards && !(this instanceof net.minecraft.world.entity.player.Player)) { scoreboardteam = null; } // Paper - Perf: Disable Scoreboards for non players by default
boolean flag = scoreboardteam != null && scoreboard.addPlayerToTeam(this.getStringUUID(), scoreboardteam);
if (!flag) {
@@ -819,9 +922,32 @@
}
+ // CraftBukkit start
+ private boolean isTickingEffects = false;
+ private List<ProcessableEffect> effectsToProcess = Lists.newArrayList();
+
+ private static class ProcessableEffect {
+
+ private Holder<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(Holder<MobEffect> type, EntityPotionEffectEvent.Cause cause) {
+ this.type = type;
+ this.cause = cause;
+ }
+ }
+ // CraftBukkit end
+
protected void tickEffects() {
Iterator<Holder<MobEffect>> iterator = this.activeEffects.keySet().iterator();
+ this.isTickingEffects = true; // CraftBukkit
try {
while (iterator.hasNext()) {
Holder<MobEffect> holder = (Holder) iterator.next();
@@ -831,6 +957,12 @@
this.onEffectUpdated(mobeffect, 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.onEffectsRemoved(List.of(mobeffect));
}
@@ -841,6 +973,17 @@
} catch (ConcurrentModificationException concurrentmodificationexception) {
;
}
+ // CraftBukkit start
+ this.isTickingEffects = false;
+ for (ProcessableEffect e : this.effectsToProcess) {
+ if (e.effect != null) {
+ this.addEffect(e.effect, e.cause);
+ } else {
+ this.removeEffect(e.type, e.cause);
+ }
+ }
+ this.effectsToProcess.clear();
+ // CraftBukkit end
if (this.effectsDirty) {
if (!this.level().isClientSide) {
@@ -921,7 +1064,7 @@
}
public boolean canAttack(LivingEntity target) {
- return target instanceof Player && this.level().getDifficulty() == Difficulty.PEACEFUL ? false : target.canBeSeenAsEnemy();
+ return target instanceof net.minecraft.world.entity.player.Player && this.level().getDifficulty() == Difficulty.PEACEFUL ? false : target.canBeSeenAsEnemy();
}
public boolean canBeSeenAsEnemy() {
@@ -952,17 +1095,36 @@
this.entityData.set(LivingEntity.DATA_EFFECT_PARTICLES, List.of());
}
+ // CraftBukkit start
public boolean removeAllEffects() {
+ return this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
+ }
+
+ public boolean removeAllEffects(EntityPotionEffectEvent.Cause cause) {
+ // CraftBukkit end
if (this.level().isClientSide) {
return false;
} else if (this.activeEffects.isEmpty()) {
return false;
} else {
- Map<Holder<MobEffect>, MobEffectInstance> map = Maps.newHashMap(this.activeEffects);
+ // CraftBukkit start
+ List<MobEffectInstance> toRemove = new LinkedList<>();
+ Iterator<MobEffectInstance> iterator = this.activeEffects.values().iterator();
- this.activeEffects.clear();
- this.onEffectsRemoved(map.values());
- return true;
+ while (iterator.hasNext()) {
+ MobEffectInstance effect = iterator.next();
+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED);
+ if (event.isCancelled()) {
+ continue;
+ }
+
+ iterator.remove();
+ toRemove.add(effect);
+ }
+
+ this.onEffectsRemoved(toRemove);
+ return !toRemove.isEmpty();
+ // CraftBukkit end
}
}
@@ -987,24 +1149,63 @@
return this.addEffect(effect, (Entity) null);
}
+ // CraftBukkit start
+ public boolean addEffect(MobEffectInstance mobeffect, EntityPotionEffectEvent.Cause cause) {
+ return this.addEffect(mobeffect, (Entity) null, cause);
+ }
+
public boolean addEffect(MobEffectInstance effect, @Nullable Entity source) {
- if (!this.canBeAffected(effect)) {
+ return this.addEffect(effect, source, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
+ }
+
+ public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) {
+ // Paper start - Don't fire sync event during generation
+ return this.addEffect(mobeffect, entity, cause, true);
+ }
+ public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) {
+ // Paper end - Don't fire sync event during generation
+ // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
+ if (this.isTickingEffects) {
+ this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause));
+ return true;
+ }
+ // CraftBukkit end
+
+ if (!this.canBeAffected(mobeffect)) {
return false;
} else {
- MobEffectInstance mobeffect1 = (MobEffectInstance) this.activeEffects.get(effect.getEffect());
+ MobEffectInstance mobeffect1 = (MobEffectInstance) this.activeEffects.get(mobeffect.getEffect());
boolean flag = false;
+ // CraftBukkit start
+ boolean override = false;
+ if (mobeffect1 != null) {
+ override = new MobEffectInstance(mobeffect1).update(mobeffect);
+ }
+
+ if (fireEvent) { // Paper - Don't fire sync event during generation
+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override);
+ override = event.isOverride(); // Paper - Don't fire sync event during generation
+ if (event.isCancelled()) {
+ return false;
+ }
+ } // Paper - Don't fire sync event during generation
+ // CraftBukkit end
+
if (mobeffect1 == null) {
- this.activeEffects.put(effect.getEffect(), effect);
- this.onEffectAdded(effect, source);
+ this.activeEffects.put(mobeffect.getEffect(), mobeffect);
+ this.onEffectAdded(mobeffect, entity);
flag = true;
- effect.onEffectAdded(this);
- } else if (mobeffect1.update(effect)) {
- this.onEffectUpdated(mobeffect1, true, source);
+ mobeffect.onEffectAdded(this);
+ // CraftBukkit start
+ } else if (override) { // Paper - Don't fire sync event during generation
+ mobeffect1.update(mobeffect);
+ this.onEffectUpdated(mobeffect1, true, entity);
+ // CraftBukkit end
flag = true;
}
- effect.onEffectStarted(this);
+ mobeffect.onEffectStarted(this);
return flag;
}
}
@@ -1031,14 +1232,40 @@
return this.getType().is(EntityTypeTags.INVERTED_HEALING_AND_HARM);
}
+ // CraftBukkit start
@Nullable
public MobEffectInstance removeEffectNoUpdate(Holder<MobEffect> effect) {
- return (MobEffectInstance) this.activeEffects.remove(effect);
+ return this.removeEffectNoUpdate(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
}
+ @Nullable
+ public MobEffectInstance removeEffectNoUpdate(Holder<MobEffect> holder, EntityPotionEffectEvent.Cause cause) {
+ if (this.isTickingEffects) {
+ this.effectsToProcess.add(new ProcessableEffect(holder, cause));
+ return null;
+ }
+
+ MobEffectInstance effect = this.activeEffects.get(holder);
+ if (effect == null) {
+ return null;
+ }
+
+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause);
+ if (event.isCancelled()) {
+ return null;
+ }
+
+ return (MobEffectInstance) this.activeEffects.remove(holder);
+ }
+
public boolean removeEffect(Holder<MobEffect> effect) {
- MobEffectInstance mobeffect = this.removeEffectNoUpdate(effect);
+ return this.removeEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
+ }
+ public boolean removeEffect(Holder<MobEffect> holder, EntityPotionEffectEvent.Cause cause) {
+ MobEffectInstance mobeffect = this.removeEffectNoUpdate(holder, cause);
+ // CraftBukkit end
+
if (mobeffect != null) {
this.onEffectsRemoved(List.of(mobeffect));
return true;
@@ -1142,20 +1369,65 @@
}
+ // CraftBukkit start - Delegate so we can handle providing a reason for health being regained
public void heal(float amount) {
+ this.heal(amount, EntityRegainHealthEvent.RegainReason.CUSTOM);
+ }
+
+ public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason) {
+ // Paper start - Forward
+ heal(f, regainReason, false);
+ }
+
+ public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason, boolean isFastRegen) {
+ // Paper end
float f1 = this.getHealth();
if (f1 > 0.0F) {
- this.setHealth(f1 + amount);
+ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason, isFastRegen); // Paper
+ // 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 health) {
+ // Paper start - Check for NaN
+ if (Float.isNaN(health)) { health = getMaxHealth(); if (this.valid) {
+ System.err.println("[NAN-HEALTH] " + getScoreboardName() + " had NaN health set");
+ } } // Paper end - Check for NaN
+ // 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()));
}
@@ -1167,7 +1439,7 @@
public boolean hurtServer(ServerLevel world, DamageSource source, float amount) {
if (this.isInvulnerableTo(world, source)) {
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 (source.is(DamageTypeTags.IS_FIRE) && this.hasEffect(MobEffects.FIRE_RESISTANCE)) {
return false;
@@ -1182,10 +1454,11 @@
}
float f1 = amount;
- boolean flag = false;
+ boolean flag = amount > 0.0F && this.isDamageSourceBlocked(source); // Copied from below
float f2 = 0.0F;
- if (amount > 0.0F && this.isDamageSourceBlocked(source)) {
+ // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) for get f and actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage
+ if (false && amount > 0.0F && this.isDamageSourceBlocked(source)) {
this.hurtCurrentlyUsedShield(amount);
f2 = amount;
amount = 0.0F;
@@ -1202,15 +1475,26 @@
flag = true;
}
- if (source.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) {
+ // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) for get f
+ if (false && source.is(DamageTypeTags.IS_FREEZING) && this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) {
amount *= 5.0F;
}
- if (source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+ // CraftBukkit - Moved into handleEntityDamage(DamageSource, float) for get f and actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage
+ if (false && source.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
this.hurtHelmet(source, amount);
amount *= 0.75F;
}
+ // CraftBukkit start
+ EntityDamageEvent event = this.handleEntityDamage(source, amount);
+ amount = 0;
+ amount += (float) event.getDamage(DamageModifier.BASE);
+ amount += (float) event.getDamage(DamageModifier.BLOCKING);
+ amount += (float) event.getDamage(DamageModifier.FREEZING);
+ amount += (float) event.getDamage(DamageModifier.HARD_HAT);
+ // CraftBukkit end
+
this.walkAnimation.setSpeed(1.5F);
if (Float.isNaN(amount) || Float.isInfinite(amount)) {
amount = Float.MAX_VALUE;
@@ -1218,18 +1502,27 @@
boolean flag1 = true;
- if ((float) this.invulnerableTime > 10.0F && !source.is(DamageTypeTags.BYPASSES_COOLDOWN)) {
+ 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(world, source, amount - this.lastHurt);
+ // CraftBukkit start
+ if (!this.actuallyHurt(world, source, (float) event.getFinalDamage() - this.lastHurt, event)) {
+ return false;
+ }
+ // CraftBukkit end
this.lastHurt = amount;
flag1 = false;
} else {
+ // CraftBukkit start
+ if (!this.actuallyHurt(world, source, (float) event.getFinalDamage(), event)) {
+ return false;
+ }
this.lastHurt = amount;
- this.invulnerableTime = 20;
- this.actuallyHurt(world, source, amount);
+ this.invulnerableTime = this.invulnerableDuration; // CraftBukkit - restore use of maxNoDamageTicks
+ // this.actuallyHurt(worldserver, damagesource, f);
+ // CraftBukkit end
this.hurtDuration = 10;
this.hurtTime = this.hurtDuration;
}
@@ -1243,7 +1536,7 @@
world.broadcastDamageEvent(this, source);
}
- if (!source.is(DamageTypeTags.NO_IMPACT) && (!flag || amount > 0.0F)) {
+ if (!source.is(DamageTypeTags.NO_IMPACT) && !flag) { // CraftBukkit - Prevent marking hurt if the damage is blocked
this.markHurt();
}
@@ -1263,7 +1556,7 @@
d1 = source.getSourcePosition().z() - this.getZ();
}
- this.knockback(0.4000000059604645D, d0, d1);
+ this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
if (!flag) {
this.indicateDamage(d0, d1);
}
@@ -1272,17 +1565,18 @@
if (this.isDeadOrDying()) {
if (!this.checkTotemDeathProtection(source)) {
- if (flag1) {
- this.makeSound(this.getDeathSound());
- }
+ // 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);
}
- boolean flag2 = !flag || amount > 0.0F;
+ boolean flag2 = !flag; // CraftBukkit - Ensure to return false if damage is blocked
if (flag2) {
this.lastDamageSource = source;
@@ -1329,10 +1623,10 @@
}
@Nullable
- protected Player resolvePlayerResponsibleForDamage(DamageSource damageSource) {
+ protected net.minecraft.world.entity.player.Player resolvePlayerResponsibleForDamage(DamageSource damageSource) {
Entity entity = damageSource.getEntity();
- if (entity instanceof Player entityhuman) {
+ if (entity instanceof net.minecraft.world.entity.player.Player entityhuman) {
this.lastHurtByPlayerTime = 100;
this.lastHurtByPlayer = entityhuman;
return entityhuman;
@@ -1342,8 +1636,8 @@
this.lastHurtByPlayerTime = 100;
LivingEntity entityliving = entitywolf.getOwner();
- if (entityliving instanceof Player) {
- Player entityhuman1 = (Player) entityliving;
+ if (entityliving instanceof net.minecraft.world.entity.player.Player) {
+ net.minecraft.world.entity.player.Player entityhuman1 = (net.minecraft.world.entity.player.Player) entityliving;
this.lastHurtByPlayer = entityhuman1;
} else {
@@ -1363,7 +1657,7 @@
}
protected void blockedByShield(LivingEntity target) {
- target.knockback(0.5D, target.getX() - this.getX(), target.getZ() - this.getZ());
+ target.knockback(0.5D, target.getX() - this.getX(), target.getZ() - this.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.SHIELD_BLOCK); // CraftBukkit // Paper - fix attacker & knockback events
}
private boolean checkTotemDeathProtection(DamageSource source) {
@@ -1375,20 +1669,33 @@
InteractionHand[] aenumhand = InteractionHand.values();
int i = aenumhand.length;
+ // CraftBukkit start
+ InteractionHand hand = null;
+ ItemStack itemstack1 = ItemStack.EMPTY;
for (int j = 0; j < i; ++j) {
InteractionHand enumhand = aenumhand[j];
- ItemStack itemstack1 = this.getItemInHand(enumhand);
+ itemstack1 = this.getItemInHand(enumhand);
deathprotection = (DeathProtection) itemstack1.get(DataComponents.DEATH_PROTECTION);
if (deathprotection != null) {
+ hand = enumhand; // CraftBukkit
itemstack = itemstack1.copy();
- itemstack1.shrink(1);
+ // itemstack1.subtract(1); // CraftBukkit
break;
}
}
- if (itemstack != null) {
- if (this instanceof ServerPlayer) {
+ org.bukkit.inventory.EquipmentSlot handSlot = (hand != null) ? org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(hand) : null;
+ EntityResurrectEvent event = new EntityResurrectEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), handSlot);
+ event.setCancelled(itemstack == null);
+ this.level().getCraftServer().getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ if (!itemstack1.isEmpty() && itemstack != null) { // Paper - only reduce item if actual totem was found
+ itemstack1.shrink(1);
+ }
+ if (itemstack != null && this instanceof ServerPlayer) {
+ // CraftBukkit end
ServerPlayer entityplayer = (ServerPlayer) this;
entityplayer.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
@@ -1468,6 +1775,7 @@
Entity entity = damageSource.getEntity();
LivingEntity entityliving = this.getKillCredit();
+ /* // Paper - move down to make death event cancellable - this is the awardKillScore below
if (entityliving != null) {
entityliving.awardKillScore(this, damageSource);
}
@@ -1477,26 +1785,61 @@
}
if (!this.level().isClientSide && this.hasCustomName()) {
- LivingEntity.LOGGER.info("Named entity {} died: {}", this, this.getCombatTracker().getDeathMessage().getString());
+ 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(worldserver, damageSource);
+ if (deathEvent == null || !deathEvent.isCancelled()) {
+ //if (entityliving != null) { // Paper - Fix item duplication and teleport issues; moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent
+ // entityliving.awardKillScore(this, 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(worldserver, damageSource);
+ } else {
+ this.dead = false;
+ this.setHealth((float) deathEvent.getReviveHealth());
+ }
+ // Paper end
this.createWitherRose(entityliving);
}
+ // Paper start
+ if (this.dead) { // Paper
this.level().broadcastEntityEvent(this, (byte) 3);
- }
this.setPose(Pose.DYING);
+ }
+ // Paper end
}
}
@@ -1506,20 +1849,28 @@
if (world instanceof ServerLevel worldserver) {
boolean flag = false;
- if (adversary instanceof WitherBoss) {
+ if (this.dead && adversary instanceof WitherBoss) { // Paper
if (worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
BlockPos blockposition = this.blockPosition();
BlockState iblockdata = Blocks.WITHER_ROSE.defaultBlockState();
if (this.level().getBlockState(blockposition).isAir() && iblockdata.canSurvive(this.level(), blockposition)) {
- this.level().setBlock(blockposition, iblockdata, 3);
- flag = true;
+ // 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 entityitem = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), new ItemStack(Items.WITHER_ROSE));
+ // 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);
}
}
@@ -1527,27 +1878,60 @@
}
}
- protected void dropAllDeathLoot(ServerLevel world, DamageSource damageSource) {
+ // Paper start
+ protected boolean clearEquipmentSlots = true;
+ protected Set<EquipmentSlot> clearedEquipmentSlots = new java.util.HashSet<>();
+ protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel world, DamageSource damageSource) {
+ // Paper end
boolean flag = this.lastHurtByPlayerTime > 0;
+ this.dropEquipment(world); // CraftBukkit - from below
if (this.shouldDropLoot() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
this.dropFromLootTable(world, damageSource, flag);
+ // Paper start
+ final boolean prev = this.clearEquipmentSlots;
+ this.clearEquipmentSlots = false;
+ this.clearedEquipmentSlots.clear();
+ // Paper end
this.dropCustomDeathLoot(world, damageSource, flag);
+ this.clearEquipmentSlots = prev; // Paper
}
-
- this.dropEquipment(world);
+ // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, damageSource, this.drops, () -> {
+ final LivingEntity entityliving = this.getKillCredit();
+ if (entityliving != null) {
+ entityliving.awardKillScore(this, damageSource);
+ }
+ }); // Paper end
+ this.postDeathDropItems(deathEvent); // Paper
+ this.drops = new ArrayList<>();
+ // CraftBukkit end
+
+ // this.dropEquipment(worldserver);// CraftBukkit - moved up
this.dropExperience(world, damageSource.getEntity());
+ return deathEvent; // Paper
}
protected void dropEquipment(ServerLevel world) {}
+ 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
- protected void dropExperience(ServerLevel world, @Nullable Entity attacker) {
- if (!this.wasExperienceConsumed() && (this.isAlwaysExperienceDropper() || this.lastHurtByPlayerTime > 0 && this.shouldDropExperience() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT))) {
- ExperienceOrb.award(world, this.position(), this.getExperienceReward(world, attacker));
+ public int getExpReward(ServerLevel worldserver, @Nullable Entity entity) { // CraftBukkit
+ if (!this.wasExperienceConsumed() && (this.isAlwaysExperienceDropper() || this.lastHurtByPlayerTime > 0 && this.shouldDropExperience() && worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT))) {
+ return this.getExperienceReward(worldserver, entity); // CraftBukkit }
}
+ return 0; // CraftBukkit
}
+ protected void dropExperience(ServerLevel world, @Nullable Entity attacker) {
+ // CraftBukkit start - Update getExpReward() above if the removed if() changes!
+ if (!(this instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon)) { // CraftBukkit - SPIGOT-2420: Special case ender dragon will drop the xp over time
+ ExperienceOrb.award(world, this.position(), this.expToDrop, this instanceof ServerPlayer ? org.bukkit.entity.ExperienceOrb.SpawnReason.PLAYER_DEATH : org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, attacker, this); // Paper
+ this.expToDrop = 0;
+ }
+ // CraftBukkit end
+ }
+
protected void dropCustomDeathLoot(ServerLevel world, DamageSource source, boolean causedByPlayer) {}
public long getLootTableSeed() {
@@ -1612,19 +1996,35 @@
}
public void knockback(double strength, double x, double z) {
- strength *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE);
- if (strength > 0.0D) {
- this.hasImpulse = true;
+ // CraftBukkit start - EntityKnockbackEvent
+ this.knockback(strength, x, z, null, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.UNKNOWN); // Paper - knockback events
+ }
+ public void knockback(double d0, double d1, double d2, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause) { // Paper - knockback events
+ d0 *= 1.0D - this.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE);
+ if (true || d0 > 0.0D) { // CraftBukkit - Call event even when force is 0
+ //this.hasImpulse = true; // CraftBukkit - Move down
+
Vec3 vec3d;
- for (vec3d = this.getDeltaMovement(); x * x + z * z < 9.999999747378752E-6D; z = (Math.random() - Math.random()) * 0.01D) {
- x = (Math.random() - Math.random()) * 0.01D;
+ for (vec3d = this.getDeltaMovement(); d1 * d1 + d2 * d2 < 9.999999747378752E-6D; d2 = (Math.random() - Math.random()) * 0.01D) {
+ d1 = (Math.random() - Math.random()) * 0.01D;
}
- Vec3 vec3d1 = (new Vec3(x, 0.0D, z)).normalize().scale(strength);
+ Vec3 vec3d1 = (new Vec3(d1, 0.0D, d2)).normalize().scale(d0);
- this.setDeltaMovement(vec3d.x / 2.0D - vec3d1.x, this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + strength) : vec3d.y, vec3d.z / 2.0D - vec3d1.z);
+ // Paper start - knockback events
+ Vec3 finalVelocity = new Vec3(vec3d.x / 2.0D - vec3d1.x, this.onGround() ? Math.min(0.4D, vec3d.y / 2.0D + d0) : vec3d.y, vec3d.z / 2.0D - vec3d1.z);
+ Vec3 diff = finalVelocity.subtract(vec3d);
+ io.papermc.paper.event.entity.EntityKnockbackEvent event = CraftEventFactory.callEntityKnockbackEvent((org.bukkit.craftbukkit.entity.CraftLivingEntity) this.getBukkitEntity(), attacker, attacker, cause, d0, diff);
+ // Paper end - knockback events
+ if (event.isCancelled()) {
+ return;
+ }
+
+ this.hasImpulse = true;
+ this.setDeltaMovement(vec3d.add(event.getKnockback().getX(), event.getKnockback().getY(), event.getKnockback().getZ())); // Paper - knockback events
+ // CraftBukkit end
}
}
@@ -1683,6 +2083,20 @@
return new LivingEntity.Fallsounds(SoundEvents.GENERIC_SMALL_FALL, SoundEvents.GENERIC_BIG_FALL);
}
+ // CraftBukkit start - Add delegate methods
+ public SoundEvent getHurtSound0(DamageSource damagesource) {
+ return this.getHurtSound(damagesource);
+ }
+
+ public SoundEvent getDeathSound0() {
+ return this.getDeathSound();
+ }
+
+ public SoundEvent getFallDamageSound0(int fallHeight) {
+ return this.getFallDamageSound(fallHeight);
+ }
+ // CraftBukkit end
+
public Optional<BlockPos> getLastClimbablePos() {
return this.lastClimbablePos;
}
@@ -1757,9 +2171,14 @@
int i = this.calculateFallDamage(fallDistance, damageMultiplier);
if (i > 0) {
+ // CraftBukkit start
+ if (!this.hurtServer((ServerLevel) this.level(), damageSource, (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;
@@ -1830,7 +2249,7 @@
protected float getDamageAfterArmorAbsorb(DamageSource source, float amount) {
if (!source.is(DamageTypeTags.BYPASSES_ARMOR)) {
- this.hurtArmor(source, amount);
+ // this.hurtArmor(damagesource, f); // CraftBukkit - actuallyHurt(DamageSource, float, EntityDamageEvent) for handle damage
amount = CombatRules.getDamageAfterAbsorb(this, amount, source, (float) this.getArmorValue(), (float) this.getAttributeValue(Attributes.ARMOR_TOUGHNESS));
}
@@ -1841,7 +2260,8 @@
if (source.is(DamageTypeTags.BYPASSES_EFFECTS)) {
return amount;
} else {
- if (this.hasEffect(MobEffects.DAMAGE_RESISTANCE) && !source.is(DamageTypeTags.BYPASSES_RESISTANCE)) {
+ // CraftBukkit - Moved to handleEntityDamage(DamageSource, float)
+ if (false && this.hasEffect(MobEffects.DAMAGE_RESISTANCE) && !source.is(DamageTypeTags.BYPASSES_RESISTANCE)) {
int i = (this.getEffect(MobEffects.DAMAGE_RESISTANCE).getAmplifier() + 1) * 5;
int j = 25 - i;
float f1 = amount * (float) j;
@@ -1884,18 +2304,154 @@
}
}
- protected void actuallyHurt(ServerLevel world, DamageSource source, float amount) {
- if (!this.isInvulnerableTo(world, source)) {
- amount = this.getDamageAfterArmorAbsorb(source, amount);
- amount = this.getDamageAfterMagicAbsorb(source, amount);
- float f1 = amount;
+ // CraftBukkit start
+ private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float f) {
+ float originalDamage = f;
- amount = Math.max(amount - this.getAbsorptionAmount(), 0.0F);
- this.setAbsorptionAmount(this.getAbsorptionAmount() - (f1 - amount));
- float f2 = f1 - amount;
+ com.google.common.base.Function<Double, Double> freezing = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ if (damagesource.is(DamageTypeTags.IS_FREEZING) && LivingEntity.this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES)) {
+ return -(f - (f * 5.0F));
+ }
+ return -0.0;
+ }
+ };
+ float freezingModifier = freezing.apply((double) f).floatValue();
+ f += freezingModifier;
+ com.google.common.base.Function<Double, Double> hardHat = new com.google.common.base.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));
+ }
+ return -0.0;
+ }
+ };
+ float hardHatModifier = hardHat.apply((double) f).floatValue();
+ f += hardHatModifier;
+
+ com.google.common.base.Function<Double, Double> blocking = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ return -((LivingEntity.this.isDamageSourceBlocked(damagesource)) ? f : 0.0);
+ }
+ };
+ float blockingModifier = blocking.apply((double) f).floatValue();
+ f += blockingModifier;
+
+ com.google.common.base.Function<Double, Double> armor = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ return -(f - LivingEntity.this.getDamageAfterArmorAbsorb(damagesource, f.floatValue()));
+ }
+ };
+ float armorModifier = armor.apply((double) f).floatValue();
+ f += armorModifier;
+
+ com.google.common.base.Function<Double, Double> resistance = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ if (!damagesource.is(DamageTypeTags.BYPASSES_EFFECTS) && LivingEntity.this.hasEffect(MobEffects.DAMAGE_RESISTANCE) && !damagesource.is(DamageTypeTags.BYPASSES_RESISTANCE)) {
+ int i = (LivingEntity.this.getEffect(MobEffects.DAMAGE_RESISTANCE).getAmplifier() + 1) * 5;
+ int j = 25 - i;
+ float f1 = f.floatValue() * (float) j;
+
+ return -(f - Math.max(f1 / 25.0F, 0.0F));
+ }
+ return -0.0;
+ }
+ };
+ float resistanceModifier = resistance.apply((double) f).floatValue();
+ f += resistanceModifier;
+
+ com.google.common.base.Function<Double, Double> magic = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ return -(f - LivingEntity.this.getDamageAfterMagicAbsorb(damagesource, f.floatValue()));
+ }
+ };
+ float magicModifier = magic.apply((double) f).floatValue();
+ f += magicModifier;
+
+ com.google.common.base.Function<Double, Double> absorption = new com.google.common.base.Function<Double, Double>() {
+ @Override
+ public Double apply(Double f) {
+ return -(Math.max(f - Math.max(f - LivingEntity.this.getAbsorptionAmount(), 0.0F), 0.0F));
+ }
+ };
+ float absorptionModifier = absorption.apply((double) f).floatValue();
+
+ return CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption);
+ }
+
+ protected boolean actuallyHurt(ServerLevel worldserver, final DamageSource damagesource, float f, final EntityDamageEvent event) { // void -> boolean, add final
+ if (!this.isInvulnerableTo(worldserver, damagesource)) {
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ if (damagesource.getEntity() instanceof net.minecraft.world.entity.player.Player) {
+ // Paper start - PlayerAttackEntityCooldownResetEvent
+ //((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetAttackStrengthTicker(); // Moved from EntityHuman in order to make the cooldown reset get called after the damage event is fired
+ if (damagesource.getEntity() instanceof ServerPlayer) {
+ ServerPlayer player = (ServerPlayer) damagesource.getEntity();
+ if (new com.destroystokyo.paper.event.player.PlayerAttackEntityCooldownResetEvent(player.getBukkitEntity(), this.getBukkitEntity(), player.getAttackStrengthScale(0F)).callEvent()) {
+ player.resetAttackStrengthTicker();
+ }
+ } else {
+ ((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetAttackStrengthTicker();
+ }
+ // Paper end - PlayerAttackEntityCooldownResetEvent
+ }
+
+ // Resistance
+ if (event.getDamage(DamageModifier.RESISTANCE) < 0) {
+ float f3 = (float) -event.getDamage(DamageModifier.RESISTANCE);
+ if (f3 > 0.0F && f3 < 3.4028235E37F) {
+ if (this instanceof ServerPlayer) {
+ ((ServerPlayer) this).awardStat(Stats.DAMAGE_RESISTED, Math.round(f3 * 10.0F));
+ } else if (damagesource.getEntity() instanceof ServerPlayer) {
+ ((ServerPlayer) damagesource.getEntity()).awardStat(Stats.DAMAGE_DEALT_RESISTED, Math.round(f3 * 10.0F));
+ }
+ }
+ }
+
+ // Apply damage to helmet
+ if (damagesource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+ this.hurtHelmet(damagesource, f);
+ }
+
+ // Apply damage to armor
+ if (!damagesource.is(DamageTypeTags.BYPASSES_ARMOR)) {
+ float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT));
+ this.hurtArmor(damagesource, armorDamage);
+ }
+
+ // Apply blocking code // PAIL: steal from above
+ if (event.getDamage(DamageModifier.BLOCKING) < 0) {
+ this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
+ Entity entity = damagesource.getDirectEntity();
+
+ if (entity instanceof LivingEntity) {
+ this.blockUsingShield((LivingEntity) entity);
+ }
+ }
+
+ boolean human = this instanceof net.minecraft.world.entity.player.Player;
+ float originalDamage = (float) event.getDamage();
+ float absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION);
+ this.setAbsorptionAmount(Math.max(this.getAbsorptionAmount() - absorptionModifier, 0.0F));
+ float f2 = absorptionModifier;
+
+ if (f2 > 0.0F && f2 < 3.4028235E37F && this instanceof net.minecraft.world.entity.player.Player) {
+ ((net.minecraft.world.entity.player.Player) this).awardStat(Stats.DAMAGE_ABSORBED, Math.round(f2 * 10.0F));
+ }
+ // CraftBukkit end
+
if (f2 > 0.0F && f2 < 3.4028235E37F) {
- Entity entity = source.getEntity();
+ Entity entity = damagesource.getEntity();
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
@@ -1904,13 +2460,48 @@
}
}
- if (amount != 0.0F) {
- this.getCombatTracker().recordDamage(source, amount);
- this.setHealth(this.getHealth() - amount);
- this.setAbsorptionAmount(this.getAbsorptionAmount() - amount);
+ // CraftBukkit start
+ 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);
+ // 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, originalDamage, f, true); // Paper - fix taken/dealt param order
+ 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, originalDamage, f, true); // Paper - fix taken/dealt param order
+ }
+
+ return true;
+ } else {
+ return originalDamage > 0;
+ }
+ // CraftBukkit end
}
}
+ return false; // CraftBukkit
}
public CombatTracker getCombatTracker() {
@@ -1935,9 +2526,19 @@
}
public final void setArrowCount(int stuckArrowCount) {
- this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, stuckArrowCount);
+ // CraftBukkit start
+ this.setArrowCount(stuckArrowCount, false);
}
+ public final void setArrowCount(int i, boolean flag) {
+ ArrowBodyCountChangeEvent event = CraftEventFactory.callArrowBodyCountChangeEvent(this, 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);
}
@@ -1999,7 +2600,7 @@
this.playSound(soundeffect, this.getSoundVolume(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F);
}
- if (!(this instanceof Player)) {
+ if (!(this instanceof net.minecraft.world.entity.player.Player)) {
this.setHealth(0.0F);
this.die(this.damageSources().generic());
}
@@ -2182,6 +2783,12 @@
public abstract ItemStack getItemBySlot(EquipmentSlot slot);
+ // CraftBukkit start
+ public void setItemSlot(EquipmentSlot enumitemslot, ItemStack itemstack, boolean silent) {
+ this.setItemSlot(enumitemslot, itemstack);
+ }
+ // CraftBukkit end
+
public abstract void setItemSlot(EquipmentSlot slot, ItemStack stack);
public Iterable<ItemStack> getHandSlots() {
@@ -2292,17 +2899,29 @@
return this.hasEffect(MobEffects.JUMP) ? 0.1F * ((float) this.getEffect(MobEffects.JUMP).getAmplifier() + 1.0F) : 0.0F;
}
+ protected long lastJumpTime = 0L; // Paper - Prevent excessive velocity through repeated crits
@VisibleForTesting
public void jumpFromGround() {
float f = this.getJumpPower();
if (f > 1.0E-5F) {
Vec3 vec3d = this.getDeltaMovement();
+ // Paper start - Prevent excessive velocity through repeated crits
+ long time = System.nanoTime();
+ boolean canCrit = true;
+ if (this instanceof net.minecraft.world.entity.player.Player) {
+ canCrit = false;
+ if (time - this.lastJumpTime > (long)(0.250e9)) {
+ this.lastJumpTime = time;
+ canCrit = true;
+ }
+ }
+ // Paper end - Prevent excessive velocity through repeated crits
this.setDeltaMovement(vec3d.x, Math.max((double) f, vec3d.y), vec3d.z);
if (this.isSprinting()) {
float f1 = this.getYRot() * 0.017453292F;
-
+ if (canCrit) // Paper - Prevent excessive velocity through repeated crits
this.addDeltaMovement(new Vec3((double) (-Mth.sin(f1)) * 0.2D, 0.0D, (double) Mth.cos(f1) * 0.2D));
}
@@ -2494,7 +3113,7 @@
}
- private void travelRidden(Player controllingPlayer, Vec3 movementInput) {
+ private void travelRidden(net.minecraft.world.entity.player.Player controllingPlayer, Vec3 movementInput) {
Vec3 vec3d1 = this.getRiddenInput(controllingPlayer, movementInput);
this.tickRidden(controllingPlayer, vec3d1);
@@ -2507,13 +3126,13 @@
}
- protected void tickRidden(Player controllingPlayer, Vec3 movementInput) {}
+ protected void tickRidden(net.minecraft.world.entity.player.Player controllingPlayer, Vec3 movementInput) {}
- protected Vec3 getRiddenInput(Player controllingPlayer, Vec3 movementInput) {
+ protected Vec3 getRiddenInput(net.minecraft.world.entity.player.Player controllingPlayer, Vec3 movementInput) {
return movementInput;
}
- protected float getRiddenSpeed(Player controllingPlayer) {
+ protected float getRiddenSpeed(net.minecraft.world.entity.player.Player controllingPlayer) {
return this.getSpeed();
}
@@ -2571,7 +3190,7 @@
double d1 = Mth.clamp(motion.z, -0.15000000596046448D, 0.15000000596046448D);
double d2 = Math.max(motion.y, -0.15000000596046448D);
- if (d2 < 0.0D && !this.getInBlockState().is(Blocks.SCAFFOLDING) && this.isSuppressingSlidingDownLadder() && this instanceof Player) {
+ if (d2 < 0.0D && !this.getInBlockState().is(Blocks.SCAFFOLDING) && this.isSuppressingSlidingDownLadder() && this instanceof net.minecraft.world.entity.player.Player) {
d2 = 0.0D;
}
@@ -2586,7 +3205,7 @@
}
protected float getFlyingSpeed() {
- return this.getControllingPassenger() instanceof Player ? this.getSpeed() * 0.1F : 0.02F;
+ return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? this.getSpeed() * 0.1F : 0.02F;
}
public float getSpeed() {
@@ -2634,7 +3253,7 @@
}
}
- this.detectEquipmentUpdates();
+ this.detectEquipmentUpdatesPublic(); // CraftBukkit
if (this.tickCount % 20 == 0) {
this.getCombatTracker().recheckStatus();
}
@@ -2687,37 +3306,15 @@
gameprofilerfiller.pop();
gameprofilerfiller.push("rangeChecks");
- while (this.getYRot() - this.yRotO < -180.0F) {
- this.yRotO -= 360.0F;
- }
+ // Paper start - stop large pitch and yaw changes from crashing the server
+ this.yRotO += Math.round((this.getYRot() - this.yRotO) / 360.0F) * 360.0F;
- while (this.getYRot() - this.yRotO >= 180.0F) {
- this.yRotO += 360.0F;
- }
+ this.yBodyRotO += Math.round((this.yBodyRot - this.yBodyRotO) / 360.0F) * 360.0F;
- while (this.yBodyRot - this.yBodyRotO < -180.0F) {
- this.yBodyRotO -= 360.0F;
- }
+ this.xRotO += Math.round((this.getXRot() - this.xRotO) / 360.0F) * 360.0F;
- while (this.yBodyRot - this.yBodyRotO >= 180.0F) {
- this.yBodyRotO += 360.0F;
- }
-
- while (this.getXRot() - this.xRotO < -180.0F) {
- this.xRotO -= 360.0F;
- }
-
- while (this.getXRot() - this.xRotO >= 180.0F) {
- this.xRotO += 360.0F;
- }
-
- while (this.yHeadRot - this.yHeadRotO < -180.0F) {
- this.yHeadRotO -= 360.0F;
- }
-
- while (this.yHeadRot - this.yHeadRotO >= 180.0F) {
- this.yHeadRotO += 360.0F;
- }
+ this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F;
+ // Paper end
gameprofilerfiller.pop();
this.animStep += f2;
@@ -2741,7 +3338,7 @@
this.elytraAnimationState.tick();
}
- public void detectEquipmentUpdates() {
+ public void detectEquipmentUpdatesPublic() { // CraftBukkit
Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
if (map != null) {
@@ -2778,10 +3375,17 @@
throw new MatchException((String) null, (Throwable) null);
}
- ItemStack itemstack2 = itemstack1;
+ ItemStack itemstack2 = itemstack1; final ItemStack oldEquipment = itemstack2; // Paper - PlayerArmorChangeEvent - obfhelper
- itemstack = this.getItemBySlot(enumitemslot);
+ itemstack = this.getItemBySlot(enumitemslot); final ItemStack newEquipment = itemstack;// Paper - PlayerArmorChangeEvent - obfhelper
if (this.equipmentHasChanged(itemstack2, itemstack)) {
+ // Paper start - PlayerArmorChangeEvent
+ if (this instanceof ServerPlayer && enumitemslot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR) {
+ final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(oldEquipment);
+ final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(newEquipment);
+ new com.destroystokyo.paper.event.player.PlayerArmorChangeEvent((Player) this.getBukkitEntity(), com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.valueOf(enumitemslot.name()), oldItem, newItem).callEvent();
+ }
+ // Paper end - PlayerArmorChangeEvent
if (map == null) {
map = Maps.newEnumMap(EquipmentSlot.class);
}
@@ -2974,8 +3578,10 @@
} else if (this.isInLava() && (!this.onGround() || d3 > d4)) {
this.jumpInLiquid(FluidTags.LAVA);
} else if ((this.onGround() || flag && d3 <= d4) && this.noJumpDelay == 0) {
+ if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper - Entity Jump API
this.jumpFromGround();
this.noJumpDelay = 10;
+ } else { this.setJumping(false); } // Paper - Entity Jump API; setJumping(false) stops a potential loop
}
} else {
this.noJumpDelay = 0;
@@ -3000,7 +3606,7 @@
{
LivingEntity entityliving = this.getControllingPassenger();
- if (entityliving instanceof Player entityhuman) {
+ if (entityliving instanceof net.minecraft.world.entity.player.Player entityhuman) {
if (this.isAlive()) {
this.travelRidden(entityhuman, vec3d1);
break label112;
@@ -3017,7 +3623,7 @@
this.calculateEntityAnimation(this instanceof FlyingAnimal);
gameprofilerfiller.pop();
gameprofilerfiller.push("freezing");
- if (!this.level().isClientSide && !this.isDeadOrDying()) {
+ if (!this.level().isClientSide && !this.isDeadOrDying() && !this.freezeLocked) { // Paper - Freeze Tick Lock API
int i = this.getTicksFrozen();
if (this.isInPowderSnow && this.canFreeze()) {
@@ -3046,6 +3652,20 @@
this.pushEntities();
gameprofilerfiller.pop();
+ // Paper start - Add EntityMoveEvent
+ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) {
+ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) {
+ Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO);
+ Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+ io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone());
+ if (!event.callEvent()) {
+ this.absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch());
+ } else if (!to.equals(event.getTo())) {
+ this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch());
+ }
+ }
+ }
+ // Paper end - Add EntityMoveEvent
world = this.level();
if (world instanceof ServerLevel worldserver) {
if (this.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
@@ -3063,6 +3683,7 @@
this.checkSlowFallDistance();
if (!this.level().isClientSide) {
if (!this.canGlide()) {
+ if (this.getSharedFlag(7) != false && !CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) // CraftBukkit
this.setSharedFlag(7, false);
return;
}
@@ -3113,12 +3734,26 @@
Level world = this.level();
if (!(world instanceof ServerLevel worldserver)) {
- this.level().getEntities(EntityTypeTest.forClass(Player.class), this.getBoundingBox(), EntitySelector.pushableBy(this)).forEach(this::doPush);
+ this.level().getEntities(EntityTypeTest.forClass(net.minecraft.world.entity.player.Player.class), this.getBoundingBox(), EntitySelector.pushableBy(this)).forEach(this::doPush);
} else {
- List list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushableBy(this));
+ // Paper start - don't run getEntities if we're not going to use its result
+ if (!this.isPushable()) {
+ return;
+ }
+ net.minecraft.world.scores.Team team = this.getTeam();
+ if (team != null && team.getCollisionRule() == net.minecraft.world.scores.Team.CollisionRule.NEVER) {
+ return;
+ }
+ int i = worldserver.getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
+ if (i <= 0 && this.level().paperConfig().collisions.maxEntityCollisions <= 0) {
+ return;
+ }
+ // Paper end - don't run getEntities if we're not going to use its result
+ List list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule)); // Paper - Climbing should not bypass cramming gamerule
+
if (!list.isEmpty()) {
- int i = worldserver.getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
+ // Paper - don't run getEntities if we're not going to use its result; moved up
if (i > 0 && list.size() > i - 1 && this.random.nextInt(4) == 0) {
int j = 0;
@@ -3138,10 +3773,12 @@
}
Iterator iterator1 = list.iterator();
+ this.numCollisions = Math.max(0, this.numCollisions - this.level().paperConfig().collisions.maxEntityCollisions); // Paper - Cap entity collisions
- while (iterator1.hasNext()) {
+ while (iterator1.hasNext() && this.numCollisions < this.level().paperConfig().collisions.maxEntityCollisions) { // Paper - Cap entity collisions
Entity entity1 = (Entity) iterator1.next();
-
+ entity1.numCollisions++; // Paper - Cap entity collisions
+ this.numCollisions++; // Paper - Cap entity collisions
this.doPush(entity1);
}
}
@@ -3190,10 +3827,16 @@
@Override
public void stopRiding() {
+ // Paper start - Force entity dismount during teleportation
+ this.stopRiding(false);
+ }
+ @Override
+ public void stopRiding(boolean suppressCancellation) {
+ // Paper end - Force entity dismount during teleportation
Entity entity = this.getVehicle();
- super.stopRiding();
- if (entity != null && entity != this.getVehicle() && !this.level().isClientSide) {
+ super.stopRiding(suppressCancellation); // Paper - Force entity dismount during teleportation
+ if (entity != null && entity != this.getVehicle() && !this.level().isClientSide && entity.valid) { // Paper - don't process on world gen
this.dismountVehicle(entity);
}
@@ -3258,7 +3901,7 @@
}
public void onItemPickup(ItemEntity item) {
- Entity entity = item.getOwner();
+ Entity entity = item.thrower != null ? this.level().getGlobalPlayerByUUID(item.thrower) : null; // Paper - check global player list where appropriate
if (entity instanceof ServerPlayer) {
CriteriaTriggers.THROWN_ITEM_PICKED_UP_BY_ENTITY.trigger((ServerPlayer) entity, item.getItem(), this);
@@ -3268,7 +3911,7 @@
public void take(Entity item, int count) {
if (!item.isRemoved() && !this.level().isClientSide && (item instanceof ItemEntity || item instanceof AbstractArrow || item instanceof ExperienceOrb)) {
- ((ServerLevel) this.level()).getChunkSource().broadcast(item, new ClientboundTakeItemEntityPacket(item.getId(), this.getId(), count));
+ ((ServerLevel) this.level()).getChunkSource().broadcastAndSend(this, new ClientboundTakeItemEntityPacket(item.getId(), this.getId(), count)); // Paper - broadcast with collector as source
}
}
@@ -3284,7 +3927,8 @@
Vec3 vec3d = new Vec3(this.getX(), this.getEyeY(), this.getZ());
Vec3 vec3d1 = new Vec3(entity.getX(), entityY, entity.getZ());
- return vec3d1.distanceTo(vec3d) > 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, shapeType, fluidHandling, this)).getType() == HitResult.Type.MISS;
+ // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists
+ return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, shapeType, fluidHandling, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared
}
}
@@ -3305,15 +3949,29 @@
@Override
public boolean isPickable() {
- return !this.isRemoved();
+ return !this.isRemoved() && this.collides; // CraftBukkit
}
+ // Paper start - Climbing should not bypass cramming gamerule
@Override
public boolean isPushable() {
- return this.isAlive() && !this.isSpectator() && !this.onClimbable();
+ return this.isCollidable(this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule);
}
@Override
+ public boolean isCollidable(boolean ignoreClimbing) {
+ return this.isAlive() && !this.isSpectator() && (ignoreClimbing || !this.onClimbable()) && this.collides; // CraftBukkit
+ // Paper end - Climbing should not bypass cramming gamerule
+ }
+
+ // CraftBukkit start - collidable API
+ @Override
+ public boolean canCollideWithBukkit(Entity entity) {
+ return this.isPushable() && this.collides != this.collidableExemptions.contains(entity.getUUID());
+ }
+ // CraftBukkit end
+
+ @Override
public float getYHeadRot() {
return this.yHeadRot;
}
@@ -3342,7 +4000,7 @@
}
public final void setAbsorptionAmount(float absorptionAmount) {
- this.internalSetAbsorptionAmount(Mth.clamp(absorptionAmount, 0.0F, this.getMaxAbsorption()));
+ this.internalSetAbsorptionAmount(!Float.isNaN(absorptionAmount) ? Mth.clamp(absorptionAmount, 0.0F, this.getMaxAbsorption()) : 0.0F); // Paper - Check for NaN
}
protected void internalSetAbsorptionAmount(float absorptionAmount) {
@@ -3410,9 +4068,14 @@
}
public void startUsingItem(InteractionHand hand) {
+ // Paper start - Prevent consuming the wrong itemstack
+ this.startUsingItem(hand, false);
+ }
+ public void startUsingItem(InteractionHand hand, boolean forceUpdate) {
+ // Paper end - Prevent consuming the wrong itemstack
ItemStack itemstack = this.getItemInHand(hand);
- if (!itemstack.isEmpty() && !this.isUsingItem()) {
+ if (!itemstack.isEmpty() && !this.isUsingItem() || forceUpdate) { // Paper - Prevent consuming the wrong itemstack
this.useItem = itemstack;
this.useItemRemaining = itemstack.getUseDuration(this);
if (!this.level().isClientSide) {
@@ -3483,13 +4146,50 @@
this.releaseUsingItem();
} else {
if (!this.useItem.isEmpty() && this.isUsingItem()) {
- ItemStack itemstack = this.useItem.finishUsingItem(this.level(), this);
+ this.startUsingItem(this.getUsedItemHand(), true); // Paper - Prevent consuming the wrong itemstack
+ // CraftBukkit start - fire PlayerItemConsumeEvent
+ ItemStack itemstack;
+ PlayerItemConsumeEvent event = null; // Paper
+ if (this instanceof ServerPlayer entityPlayer) {
+ org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.useItem);
+ org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(enumhand);
+ event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem, hand); // Paper
+ this.level().getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ // Update client
+ Consumable consumable = this.useItem.get(DataComponents.CONSUMABLE);
+ if (consumable != null) {
+ consumable.cancelUsingItem(entityPlayer, this.useItem);
+ }
+ entityPlayer.getBukkitEntity().updateInventory();
+ entityPlayer.getBukkitEntity().updateScaledHealth();
+ this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use
+ 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);
+ }
+ // Paper start - save the default replacement item and change it if necessary
+ final ItemStack defaultReplacement = itemstack;
+ if (event != null && event.getReplacement() != null) {
+ itemstack = CraftItemStack.asNMSCopy(event.getReplacement());
+ }
+ // Paper end
+ // CraftBukkit end
+
if (itemstack != this.useItem) {
this.setItemInHand(enumhand, itemstack);
}
this.stopUsingItem();
+ // Paper start
+ if (this instanceof ServerPlayer) {
+ ((ServerPlayer) this).getBukkitEntity().updateInventory();
+ }
+ // Paper end
}
}
@@ -3512,6 +4212,7 @@
public void releaseUsingItem() {
if (!this.useItem.isEmpty()) {
+ if (this instanceof ServerPlayer) new io.papermc.paper.event.player.PlayerStopUsingItemEvent((Player) getBukkitEntity(), useItem.asBukkitMirror(), getTicksUsingItem()).callEvent(); // Paper - Add PlayerStopUsingItemEvent
this.useItem.releaseUsing(this.level(), this, this.getUseItemRemainingTicks());
if (this.useItem.useOnRelease()) {
this.updatingUsingItem();
@@ -3544,12 +4245,69 @@
if (this.isUsingItem() && !this.useItem.isEmpty()) {
Item item = this.useItem.getItem();
- return item.getUseAnimation(this.useItem) != ItemUseAnimation.BLOCK ? null : (item.getUseDuration(this.useItem, this) - this.useItemRemaining < 5 ? null : this.useItem);
+ return item.getUseAnimation(this.useItem) != ItemUseAnimation.BLOCK ? null : (item.getUseDuration(this.useItem, this) - this.useItemRemaining < getShieldBlockingDelay() ? null : this.useItem); // Paper - Make shield blocking delay configurable
} else {
return null;
}
+ }
+
+ // Paper start - Make shield blocking delay configurable
+ public HitResult getRayTrace(int maxDistance, ClipContext.Fluid fluidCollisionOption) {
+ if (maxDistance < 1 || maxDistance > 120) {
+ throw new IllegalArgumentException("maxDistance must be between 1-120");
+ }
+
+ Vec3 start = new Vec3(getX(), getY() + getEyeHeight(), getZ());
+ org.bukkit.util.Vector dir = getBukkitEntity().getLocation().getDirection().multiply(maxDistance);
+ Vec3 end = new Vec3(start.x + dir.getX(), start.y + dir.getY(), start.z + dir.getZ());
+ ClipContext raytrace = new ClipContext(start, end, ClipContext.Block.OUTLINE, fluidCollisionOption, this);
+
+ return this.level().clip(raytrace);
+ }
+
+ public @Nullable net.minecraft.world.phys.EntityHitResult getTargetEntity(int maxDistance) {
+ if (maxDistance < 1 || maxDistance > 120) {
+ throw new IllegalArgumentException("maxDistance must be between 1-120");
+ }
+
+ Vec3 start = this.getEyePosition(1.0F);
+ Vec3 direction = this.getLookAngle();
+ Vec3 end = start.add(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance);
+
+ List<Entity> entityList = this.level().getEntities(this, getBoundingBox().expandTowards(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).inflate(1.0D, 1.0D, 1.0D), EntitySelector.NO_SPECTATORS.and(Entity::isPickable));
+
+ double distance = 0.0D;
+ net.minecraft.world.phys.EntityHitResult result = null;
+
+ for (Entity entity : entityList) {
+ final double inflationAmount = (double) entity.getPickRadius();
+ AABB aabb = entity.getBoundingBox().inflate(inflationAmount, inflationAmount, inflationAmount);
+ Optional<Vec3> rayTraceResult = aabb.clip(start, end);
+
+ if (rayTraceResult.isPresent()) {
+ Vec3 rayTrace = rayTraceResult.get();
+ double distanceTo = start.distanceToSqr(rayTrace);
+ if (distanceTo < distance || distance == 0.0D) {
+ result = new net.minecraft.world.phys.EntityHitResult(entity, rayTrace);
+ distance = distanceTo;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public int shieldBlockingDelay = this.level().paperConfig().misc.shieldBlockingDelay;
+
+ public int getShieldBlockingDelay() {
+ return shieldBlockingDelay;
}
+ public void setShieldBlockingDelay(int shieldBlockingDelay) {
+ this.shieldBlockingDelay = shieldBlockingDelay;
+ }
+ // Paper end - Make shield blocking delay configurable
+
public boolean isSuppressingSlidingDownLadder() {
return this.isShiftKeyDown();
}
@@ -3568,12 +4326,18 @@
}
public boolean randomTeleport(double x, double y, double z, boolean particleEffects) {
+ // CraftBukkit start
+ return this.randomTeleport(x, y, z, particleEffects, 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();
- double d6 = y;
+ double d6 = d1;
boolean flag1 = false;
- BlockPos blockposition = BlockPos.containing(x, y, z);
+ BlockPos blockposition = BlockPos.containing(d0, d1, d2);
Level world = this.level();
if (world.hasChunkAt(blockposition)) {
@@ -3592,18 +4356,43 @@
}
if (flag2) {
- this.teleportTo(x, d6, z);
+ // 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(), 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 (particleEffects) {
+ if (flag) {
world.broadcastEntityEvent(this, (byte) 46);
}
@@ -3613,7 +4402,7 @@
entitycreature.getNavigation().stop();
}
- return true;
+ return Optional.of(true); // CraftBukkit
}
}
@@ -3706,7 +4495,7 @@
}
public void stopSleeping() {
- Optional optional = this.getSleepingPos();
+ Optional<BlockPos> optional = this.getSleepingPos(); // CraftBukkit - decompile error
Level world = this.level();
java.util.Objects.requireNonNull(world);
@@ -3718,9 +4507,9 @@
this.level().setBlock(blockposition, (BlockState) iblockdata.setValue(BedBlock.OCCUPIED, false), 3);
Vec3 vec3d = (Vec3) BedBlock.findStandUpPosition(this.getType(), this.level(), blockposition, enumdirection, this.getYRot()).orElseGet(() -> {
- BlockPosition blockposition1 = blockposition.above();
+ BlockPos blockposition1 = blockposition.above();
- return new Vec3D((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.1D, (double) blockposition1.getZ() + 0.5D);
+ return new Vec3((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.1D, (double) blockposition1.getZ() + 0.5D);
});
Vec3 vec3d1 = Vec3.atBottomCenterOf(blockposition).subtract(vec3d).normalize();
float f = (float) Mth.wrapDegrees(Mth.atan2(vec3d1.z, vec3d1.x) * 57.2957763671875D - 90.0D);
@@ -3740,7 +4529,7 @@
@Nullable
public Direction getBedOrientation() {
- BlockPos blockposition = (BlockPos) this.getSleepingPos().orElse((Object) null);
+ BlockPos blockposition = (BlockPos) this.getSleepingPos().orElse(null); // CraftBukkit - decompile error
return blockposition != null ? BedBlock.getBedOrientation(this.level(), blockposition) : null;
}
@@ -3905,7 +4694,7 @@
public float maxUpStep() {
float f = (float) this.getAttributeValue(Attributes.STEP_HEIGHT);
- return this.getControllingPassenger() instanceof Player ? Math.max(f, 1.0F) : f;
+ return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? Math.max(f, 1.0F) : f;
}
@Override