2024-12-11 22:26:55 +01:00
--- 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;
2024-11-11 21:35:27 +01:00
@@ -135,6 +136,30 @@
import net.minecraft.world.scores.PlayerTeam;
2024-12-11 22:26:55 +01:00
import net.minecraft.world.scores.Scoreboard;
import org.slf4j.Logger;
2024-11-11 21:35:27 +01:00
+
2024-12-11 22:26:55 +01:00
+// 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
2024-11-11 21:35:27 +01:00
2024-12-11 22:26:55 +01:00
public abstract class LivingEntity extends Entity implements Attackable {
2016-03-03 11:00:11 +01:00
@@ -174,7 +199,7 @@
2024-12-11 22:26:55 +01:00
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);
2016-03-03 11:00:11 +01:00
@@ -210,7 +235,7 @@
2024-12-11 22:26:55 +01:00
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;
2021-09-15 20:44:22 +02:00
@@ -260,7 +285,30 @@
2024-12-11 22:26:55 +01:00
protected boolean skipDropExperience;
private final EnumMap<EquipmentSlot, Reference2ObjectMap<Enchantment, Set<EnchantmentLocationBasedEffect>>> activeLocationDependentEnchantments;
protected float appliedScale;
+ // CraftBukkit start
+ public int expToDrop;
2022-03-22 17:34:41 +01:00
+ public ArrayList<DefaultDrop> drops = new ArrayList<>(); // Paper - Restore vanilla drops behavior
2024-12-11 22:26:55 +01:00
+ public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
+ public boolean collides = true;
+ public Set<UUID> collidableExemptions = new HashSet<>();
+ public boolean bukkitPickUpLoot;
2016-03-29 02:55:47 +02:00
+ public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper
2018-08-21 02:39:35 +02:00
+ public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event
2021-09-15 20:44:22 +02:00
+ public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
2020-02-12 04:56:48 +01:00
2024-12-11 22:26:55 +01:00
+ @Override
+ public float getBukkitYaw() {
+ return this.getYHeadRot();
+ }
+ // CraftBukkit end
2024-11-02 08:16:11 +01:00
+ // Spigot start
+ public void inactiveTick()
+ {
+ super.inactiveTick();
+ ++this.noActionTime; // Above all the floats
+ }
+ // Spigot end
2020-02-12 04:56:48 +01:00
+
2024-12-11 22:26:55 +01:00
protected LivingEntity(EntityType<? extends LivingEntity> type, Level world) {
super(type, world);
2020-02-12 04:56:48 +01:00
this.lastHandItemStacks = NonNullList.withSize(2, ItemStack.EMPTY);
2021-09-15 20:44:22 +02:00
@@ -276,7 +324,9 @@
2024-12-11 22:26:55 +01:00
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();
2021-09-15 20:44:22 +02:00
@@ -356,7 +406,13 @@
2024-12-11 22:26:55 +01:00
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
}
}
}
2021-09-15 20:44:22 +02:00
@@ -402,7 +458,7 @@
2024-12-11 22:26:55 +01:00
}
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;
2021-09-15 20:44:22 +02:00
@@ -424,7 +480,7 @@
2024-12-11 22:26:55 +01:00
}
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()));
2021-09-15 20:44:22 +02:00
@@ -573,7 +629,7 @@
2024-12-11 22:26:55 +01:00
++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
}
}
2021-09-15 20:44:22 +02:00
@@ -629,7 +685,7 @@
2024-12-11 22:26:55 +01:00
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;
}
2021-09-15 20:44:22 +02:00
@@ -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 @@
2024-12-11 22:26:55 +01:00
}
public void onEquipItem(EquipmentSlot slot, ItemStack oldStack, ItemStack newStack) {
2024-07-08 22:01:08 +02:00
- if (!this.level().isClientSide() && !this.isSpectator()) {
- boolean flag = newStack.isEmpty() && oldStack.isEmpty();
2024-11-18 20:27:58 +01:00
-
- if (!flag && !ItemStack.isSameItemSameComponents(oldStack, newStack) && !this.firstTick) {
- Equippable equippable = (Equippable) newStack.get(DataComponents.EQUIPPABLE);
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
+ this.onEquipItem(slot, oldStack, newStack, false);
+ }
2018-11-15 14:38:37 +01:00
2024-11-18 20:27:58 +01:00
- 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());
2024-07-08 22:01:08 +02:00
+ public void onEquipItem(EquipmentSlot enumitemslot, ItemStack itemstack, ItemStack itemstack1, boolean silent) {
+ // CraftBukkit end
+ if (!this.level().isClientSide() && !this.isSpectator()) {
+ boolean flag = itemstack1.isEmpty() && itemstack.isEmpty();
2024-11-18 20:27:58 +01:00
+
2024-07-08 22:01:08 +02:00
+ if (!flag && !ItemStack.isSameItemSameComponents(itemstack, itemstack1) && !this.firstTick) {
+ Equippable equippable = (Equippable) itemstack1.get(DataComponents.EQUIPPABLE);
+
2024-12-11 22:26:55 +01:00
+ 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);
}
2021-09-15 20:44:22 +02:00
@@ -699,17 +761,24 @@
2024-12-11 22:26:55 +01:00
@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();
}
2021-09-15 20:44:22 +02:00
@@ -722,11 +791,17 @@
2024-12-11 22:26:55 +01:00
mobeffect.onMobRemoved(world, this, reason);
}
+ this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DEATH); // CraftBukkit
this.activeEffects.clear();
}
2021-09-15 20:44:22 +02:00
@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 @@
2015-09-27 07:18:02 +02:00
@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
2021-09-15 20:44:22 +02:00
+ // 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
2015-09-27 07:18:02 +02:00
if (nbt.contains("attributes", 9) && this.level() != null && !this.level().isClientSide) {
this.getAttributes().load(nbt.getList("attributes", 10));
}
2021-09-15 20:44:22 +02:00
@@ -781,6 +872,17 @@
2018-09-04 01:20:03 +02:00
}
}
2024-12-11 22:26:55 +01:00
+ // 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());
2018-09-04 01:20:03 +02:00
+ }
+ }
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2018-09-04 01:20:03 +02:00
+
2024-12-11 22:26:55 +01:00
if (nbt.contains("Health", 99)) {
this.setHealth(nbt.getFloat("Health"));
2018-09-04 01:20:03 +02:00
}
2021-09-15 20:44:22 +02:00
@@ -792,6 +894,7 @@
2016-03-09 05:25:45 +01:00
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) {
2024-07-08 22:01:08 +02:00
@@ -806,22 +909,47 @@
2024-01-04 13:49:14 +01:00
if (nbt.contains("SleepingX", 99) && nbt.contains("SleepingY", 99) && nbt.contains("SleepingZ", 99)) {
BlockPos blockposition = new BlockPos(nbt.getInt("SleepingX"), nbt.getInt("SleepingY"), nbt.getInt("SleepingZ"));
+ if (this.position().distanceToSqr(blockposition.getX(), blockposition.getY(), blockposition.getZ()) < 16 * 16) { // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong
this.setSleepingPos(blockposition);
this.entityData.set(LivingEntity.DATA_POSE, Pose.SLEEPING);
if (!this.firstTick) {
this.setPosToBed(blockposition);
}
+ } // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong
}
2021-09-15 20:44:22 +02:00
2024-01-04 13:49:14 +01:00
if (nbt.contains("Brain", 10)) {
2024-07-08 22:01:08 +02:00
this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain")));
+ }
+
+ }
+
2024-12-11 22:26:55 +01:00
+ // 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;
2024-07-08 22:01:08 +02:00
}
2024-12-11 22:26:55 +01:00
+ private ProcessableEffect(Holder<MobEffect> type, EntityPotionEffectEvent.Cause cause) {
+ this.type = type;
+ this.cause = cause;
+ }
2024-07-08 22:01:08 +02:00
}
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2024-07-08 22:01:08 +02:00
2024-12-11 22:26:55 +01:00
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();
2024-01-04 13:49:14 +01:00
@@ -831,6 +959,12 @@
2024-12-11 22:26:55 +01:00
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));
}
2024-01-04 13:49:14 +01:00
@@ -841,6 +975,17 @@
2024-12-11 22:26:55 +01:00
} catch (ConcurrentModificationException concurrentmodificationexception) {
;
2017-01-23 00:07:56 +01:00
}
2024-12-11 22:26:55 +01:00
+ // 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);
+ }
2017-01-23 00:07:56 +01:00
+ }
2024-12-11 22:26:55 +01:00
+ this.effectsToProcess.clear();
+ // CraftBukkit end
if (this.effectsDirty) {
if (!this.level().isClientSide) {
2024-01-04 13:49:14 +01:00
@@ -921,7 +1066,7 @@
2024-12-11 22:26:55 +01:00
}
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() {
2024-01-04 13:49:14 +01:00
@@ -952,17 +1097,36 @@
2024-12-11 22:26:55 +01:00
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
}
}
2024-01-04 13:49:14 +01:00
@@ -987,24 +1151,63 @@
2019-08-19 16:42:35 +02:00
return this.addEffect(effect, (Entity) null);
2020-03-27 03:44:50 +01:00
}
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
+ public boolean addEffect(MobEffectInstance mobeffect, EntityPotionEffectEvent.Cause cause) {
+ return this.addEffect(mobeffect, (Entity) null, cause);
2020-03-27 03:44:50 +01:00
+ }
+
2024-12-11 22:26:55 +01:00
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) {
2023-11-23 19:33:25 +01:00
+ // 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
2021-08-26 05:17:12 +02:00
+ // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
2024-12-11 22:26:55 +01:00
+ 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);
+ }
+
2023-11-23 19:33:25 +01:00
+ if (fireEvent) { // Paper - Don't fire sync event during generation
2024-12-11 22:26:55 +01:00
+ EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override);
2023-11-23 19:33:25 +01:00
+ override = event.isOverride(); // Paper - Don't fire sync event during generation
2024-12-11 22:26:55 +01:00
+ if (event.isCancelled()) {
+ return false;
+ }
2023-11-23 19:33:25 +01:00
+ } // Paper - Don't fire sync event during generation
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
+
2021-09-15 20:44:22 +02:00
if (mobeffect1 == null) {
- this.activeEffects.put(effect.getEffect(), effect);
- this.onEffectAdded(effect, source);
2024-12-11 22:26:55 +01:00
+ 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
2023-11-23 19:33:25 +01:00
+ } else if (override) { // Paper - Don't fire sync event during generation
2024-12-11 22:26:55 +01:00
+ mobeffect1.update(mobeffect);
+ this.onEffectUpdated(mobeffect1, true, entity);
+ // CraftBukkit end
flag = true;
}
- effect.onEffectStarted(this);
+ mobeffect.onEffectStarted(this);
return flag;
}
}
2021-12-01 12:36:25 +01:00
@@ -1031,14 +1234,40 @@
2024-12-11 22:26:55 +01:00
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);
2020-04-16 02:56:07 +02:00
}
2024-12-11 22:26:55 +01:00
+ @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);
2020-04-16 02:56:07 +02:00
+ }
+
2024-12-11 22:26:55 +01:00
public boolean removeEffect(Holder<MobEffect> effect) {
- MobEffectInstance mobeffect = this.removeEffectNoUpdate(effect);
+ return this.removeEffect(effect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN);
+ }
2021-12-01 12:36:25 +01:00
2024-12-11 22:26:55 +01:00
+ public boolean removeEffect(Holder<MobEffect> holder, EntityPotionEffectEvent.Cause cause) {
+ MobEffectInstance mobeffect = this.removeEffectNoUpdate(holder, cause);
+ // CraftBukkit end
2021-12-01 12:36:25 +01:00
+
2024-12-11 22:26:55 +01:00
if (mobeffect != null) {
this.onEffectsRemoved(List.of(mobeffect));
2021-12-01 12:36:25 +01:00
return true;
2024-01-04 13:49:14 +01:00
@@ -1142,20 +1371,65 @@
2024-12-11 22:26:55 +01:00
}
+ // 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) {
2016-04-22 08:43:11 +02:00
+ // Paper start - Forward
+ heal(f, regainReason, false);
+ }
+
+ public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason, boolean isFastRegen) {
+ // Paper end
2024-12-11 22:26:55 +01:00
float f1 = this.getHealth();
if (f1 > 0.0F) {
- this.setHealth(f1 + amount);
2016-04-22 08:43:11 +02:00
+ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason, isFastRegen); // Paper
2024-12-11 22:26:55 +01:00
+ // 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) {
2015-09-27 07:18:02 +02:00
+ // 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
2024-12-11 22:26:55 +01:00
+ // 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()));
}
2024-01-04 13:49:14 +01:00
@@ -1167,7 +1441,7 @@
2024-12-11 22:26:55 +01:00
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;
2024-04-27 18:44:53 +02:00
@@ -1181,11 +1455,12 @@
amount = 0.0F;
2024-12-11 22:26:55 +01:00
}
2024-04-27 18:44:53 +02:00
- float f1 = amount;
2024-12-11 22:26:55 +01:00
- boolean flag = false;
2024-04-27 18:44:53 +02:00
+ float f1 = amount; final float originalAmount = f1; // Paper - revert to vanilla #hurt - OBFHELPER
2024-12-11 22:26:55 +01:00
+ 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;
2024-07-08 22:01:08 +02:00
@@ -1202,15 +1477,21 @@
2024-12-11 22:26:55 +01:00
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;
}
2024-04-27 18:44:53 +02:00
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
2024-07-08 22:01:08 +02:00
+ EntityDamageEvent event; // Paper - move this into the actual invuln check....
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2024-04-27 18:44:53 +02:00
+
2024-12-11 22:26:55 +01:00
this.walkAnimation.setSpeed(1.5F);
if (Float.isNaN(amount) || Float.isInfinite(amount)) {
2024-04-27 18:44:53 +02:00
amount = Float.MAX_VALUE;
2024-07-08 22:01:08 +02:00
@@ -1218,18 +1499,38 @@
2024-12-11 22:26:55 +01:00
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);
2024-07-08 22:01:08 +02:00
+ // Paper start - only call damage event when actuallyHurt will be called - move call logic down
2024-11-11 21:35:27 +01:00
+ event = this.handleEntityDamage(source, amount, this.lastHurt); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction
2024-07-08 22:01:08 +02:00
+ amount = computeAmountFromEntityDamageEvent(event);
+ // Paper end - only call damage event when actuallyHurt will be called - move call logic down
+
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
2024-11-11 21:35:27 +01:00
+ if (!this.actuallyHurt(world, source, (float) event.getFinalDamage(), event)) { // Paper - fix invulnerability reduction in EntityDamageEvent - no longer subtract lastHurt, that is part of the damage event calc now
2024-12-11 22:26:55 +01:00
+ return false;
+ }
2024-04-27 18:44:53 +02:00
+ if (this instanceof ServerPlayer && event.getDamage() == 0 && originalAmount == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event.
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
this.lastHurt = amount;
flag1 = false;
} else {
2024-07-08 22:01:08 +02:00
+ // Paper start - only call damage event when actuallyHurt will be called - move call logic down
2024-11-11 21:35:27 +01:00
+ event = this.handleEntityDamage(source, amount, 0); // Paper - fix invulnerability reduction in EntityDamageEvent - pass lastDamage reduction (none in this branch)
2024-07-08 22:01:08 +02:00
+ amount = computeAmountFromEntityDamageEvent(event);
+ // Paper end - only call damage event when actuallyHurt will be called - move call logic down
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
+ if (!this.actuallyHurt(world, source, (float) event.getFinalDamage(), event)) {
+ return false;
+ }
2024-04-27 18:44:53 +02:00
+ if (this instanceof ServerPlayer && event.getDamage() == 0 && originalAmount == 0) return false; // Paper - revert to vanilla damage - players are not affected by damage that is 0 - skip damage if the vanilla damage is 0 and was not modified by plugins in the event.
2024-12-11 22:26:55 +01:00
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;
}
2024-07-08 22:01:08 +02:00
@@ -1243,7 +1544,7 @@
2024-12-11 22:26:55 +01:00
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();
}
2024-07-08 22:01:08 +02:00
@@ -1263,7 +1564,7 @@
2024-12-11 22:26:55 +01:00
d1 = source.getSourcePosition().z() - this.getZ();
}
- this.knockback(0.4000000059604645D, d0, d1);
2018-06-18 15:46:23 +02:00
+ 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
2024-12-11 22:26:55 +01:00
if (!flag) {
this.indicateDamage(d0, d1);
}
2024-07-08 22:01:08 +02:00
@@ -1272,17 +1573,18 @@
2018-08-21 02:39:35 +02:00
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) {
2024-12-11 22:26:55 +01:00
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;
2024-07-08 22:01:08 +02:00
@@ -1329,10 +1631,10 @@
2024-12-11 22:26:55 +01:00
}
@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;
2024-07-08 22:01:08 +02:00
@@ -1342,8 +1644,8 @@
2024-12-11 22:26:55 +01:00
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 {
2024-11-11 21:35:27 +01:00
@@ -1356,14 +1658,26 @@
2024-07-08 22:01:08 +02:00
2024-11-11 21:35:27 +01:00
return null;
}
+ }
+
2024-07-08 22:01:08 +02:00
+ // Paper start - only call damage event when actuallyHurt will be called - move out amount computation logic
+ private float computeAmountFromEntityDamageEvent(final EntityDamageEvent event) {
+ // Taken from hurt()'s craftbukkit diff.
+ float 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);
+ return amount;
2024-11-11 21:35:27 +01:00
}
2024-07-08 22:01:08 +02:00
+ // Paper end - only call damage event when actuallyHurt will be called - move out amount computation logic
2024-11-11 21:35:27 +01:00
2024-07-08 22:01:08 +02:00
protected void blockUsingShield(LivingEntity attacker) {
attacker.blockedByShield(this);
2024-12-11 22:26:55 +01:00
}
protected void blockedByShield(LivingEntity target) {
- target.knockback(0.5D, target.getX() - this.getX(), target.getZ() - this.getZ());
2018-06-18 15:46:23 +02:00
+ 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
2024-12-11 22:26:55 +01:00
}
private boolean checkTotemDeathProtection(DamageSource source) {
2024-11-18 20:27:58 +01:00
@@ -1375,20 +1689,39 @@
2024-12-11 22:26:55 +01:00
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()) {
Correctly shrink items during EntityResurrectEvent
The EntityResurrectEvent logic is supposed to locate a totem of undying
in any of the interaction slots of the player inventory and then, if the
called EntityResurrectEvent is not cancelled, shrink that item by 1,
usually reducing it to zero.
For this, the logic iterates over the items in the interaction slots and
breaks out the loop if a totem of undying was found.
However, even if no totem of undying was found, the iteration item stack
variable remains as a refernce to the last interaction slot probed.
Plugins uncancelling a EntityResurrectEvent, which is published
pre-cancelled to listeners if no totem of undying could be found,
would hence cause the server logic to shrink completely unrelated items
found in, at the writing of this patch, the players off hand slot.
This patch corrects this behaviour by only shrinking the item if a totem
of undying was found and the event was called uncancelled.
2023-01-10 21:06:42 +01:00
+ if (!itemstack1.isEmpty() && itemstack != null) { // Paper - only reduce item if actual totem was found
2024-12-11 22:26:55 +01:00
+ itemstack1.shrink(1);
+ }
2024-11-18 20:27:58 +01:00
+ // Paper start - fix NPE when pre-cancelled EntityResurrectEvent is uncancelled
+ // restore the previous behavior in that case by defaulting to vanillas totem of undying efect
+ if (deathprotection == null) {
+ deathprotection = DeathProtection.TOTEM_OF_UNDYING;
+ }
+ // Paper end - fix NPE when pre-cancelled EntityResurrectEvent is uncancelled
2024-12-11 22:26:55 +01:00
+ if (itemstack != null && this instanceof ServerPlayer) {
+ // CraftBukkit end
ServerPlayer entityplayer = (ServerPlayer) this;
entityplayer.awardStat(Stats.ITEM_USED.get(itemstack.getItem()));
2024-11-18 20:27:58 +01:00
@@ -1468,6 +1801,7 @@
2018-08-21 02:39:35 +02:00
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);
}
2024-11-18 20:27:58 +01:00
@@ -1477,26 +1811,61 @@
2019-12-14 00:26:52 +01:00
}
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
}
2018-08-21 02:39:35 +02:00
+ */ // Paper - move down to make death event cancellable - this is the awardKillScore below
2019-12-14 00:26:52 +01:00
this.dead = true;
2018-08-21 02:39:35 +02:00
- 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
2024-11-18 20:27:58 +01:00
- if (entity == null || entity.killedEntity(worldserver, this)) {
2018-08-21 02:39:35 +02:00
+ // Paper start
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(worldserver, damageSource);
+ if (deathEvent == null || !deathEvent.isCancelled()) {
2020-04-25 12:46:35 +02:00
+ //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);
+ //}
2018-08-21 02:39:35 +02:00
+ // 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
2024-11-18 20:27:58 +01:00
+
2018-08-21 02:39:35 +02:00
+ 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
}
}
2024-11-18 20:27:58 +01:00
@@ -1506,20 +1875,28 @@
2018-08-21 02:39:35 +02:00
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();
2024-12-11 22:26:55 +01:00
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);
}
}
2024-11-18 20:27:58 +01:00
@@ -1527,27 +1904,60 @@
2018-08-21 02:39:35 +02:00
}
}
- 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
2024-12-11 22:26:55 +01:00
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);
2018-08-21 02:39:35 +02:00
+ // Paper start
+ final boolean prev = this.clearEquipmentSlots;
+ this.clearEquipmentSlots = false;
+ this.clearedEquipmentSlots.clear();
+ // Paper end
2024-12-11 22:26:55 +01:00
this.dropCustomDeathLoot(world, damageSource, flag);
2018-08-21 02:39:35 +02:00
+ this.clearEquipmentSlots = prev; // Paper
2024-12-11 22:26:55 +01:00
}
2024-11-11 21:35:27 +01:00
-
- this.dropEquipment(world);
2020-04-25 12:46:35 +02:00
+ // 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
2018-08-21 02:39:35 +02:00
+ this.postDeathDropItems(deathEvent); // Paper
2024-12-11 22:26:55 +01:00
+ this.drops = new ArrayList<>();
+ // CraftBukkit end
2024-11-11 21:35:27 +01:00
+
2024-12-11 22:26:55 +01:00
+ // this.dropEquipment(worldserver);// CraftBukkit - moved up
this.dropExperience(world, damageSource.getEntity());
2018-08-21 02:39:35 +02:00
+ return deathEvent; // Paper
2024-12-11 22:26:55 +01:00
}
protected void dropEquipment(ServerLevel world) {}
2018-08-21 02:39:35 +02:00
+ 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
2024-12-11 22:26:55 +01:00
- 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 }
}
2021-09-15 20:44:22 +02:00
2024-12-11 22:26:55 +01:00
+ return 0; // CraftBukkit
2021-09-15 20:44:22 +02:00
}
2019-08-19 16:42:35 +02:00
2024-12-11 22:26:55 +01:00
+ 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
2017-12-19 22:31:46 +01:00
+ 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
2024-12-11 22:26:55 +01:00
+ this.expToDrop = 0;
+ }
+ // CraftBukkit end
2021-09-15 20:44:22 +02:00
+ }
+
2022-08-24 15:54:11 +02:00
protected void dropCustomDeathLoot(ServerLevel world, DamageSource source, boolean causedByPlayer) {}
2021-09-15 20:44:22 +02:00
public long getLootTableSeed() {
2024-11-18 20:27:58 +01:00
@@ -1612,19 +2022,35 @@
2024-12-11 22:26:55 +01:00
}
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
2018-06-18 15:46:23 +02:00
+ this.knockback(strength, x, z, null, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.UNKNOWN); // Paper - knockback events
2024-12-11 22:26:55 +01:00
+ }
2018-09-04 01:20:03 +02:00
2018-06-18 15:46:23 +02:00
+ public void knockback(double d0, double d1, double d2, @Nullable Entity attacker, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause cause) { // Paper - knockback events
2024-12-11 22:26:55 +01:00
+ 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
2018-09-04 01:20:03 +02:00
+
2024-12-11 22:26:55 +01:00
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);
2018-06-18 15:46:23 +02:00
+ // 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
2024-12-11 22:26:55 +01:00
+ if (event.isCancelled()) {
+ return;
+ }
+
+ this.hasImpulse = true;
2018-06-18 15:46:23 +02:00
+ this.setDeltaMovement(vec3d.add(event.getKnockback().getX(), event.getKnockback().getY(), event.getKnockback().getZ())); // Paper - knockback events
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
}
}
2024-11-18 20:27:58 +01:00
@@ -1683,6 +2109,20 @@
2022-08-24 15:54:11 +02:00
return new LivingEntity.Fallsounds(SoundEvents.GENERIC_SMALL_FALL, SoundEvents.GENERIC_BIG_FALL);
2021-09-15 20:44:22 +02:00
}
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start - Add delegate methods
+ public SoundEvent getHurtSound0(DamageSource damagesource) {
+ return this.getHurtSound(damagesource);
2018-06-18 15:46:23 +02:00
+ }
+
2019-08-19 16:42:35 +02:00
+ public SoundEvent getDeathSound0() {
+ return this.getDeathSound();
2022-05-04 02:41:37 +02:00
+ }
+
2024-12-11 22:26:55 +01:00
+ public SoundEvent getFallDamageSound0(int fallHeight) {
+ return this.getFallDamageSound(fallHeight);
2021-09-15 20:44:22 +02:00
+ }
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2021-09-15 20:44:22 +02:00
+
2024-12-11 22:26:55 +01:00
public Optional<BlockPos> getLastClimbablePos() {
return this.lastClimbablePos;
2021-09-15 20:44:22 +02:00
}
2024-11-18 20:27:58 +01:00
@@ -1718,7 +2158,7 @@
2024-08-26 14:01:37 +02:00
@Override
public boolean isAlive() {
- return !this.isRemoved() && this.getHealth() > 0.0F;
+ return !this.isRemoved() && this.getHealth() > 0.0F && !this.dead; // Paper - Check this.dead
}
public boolean isLookingAtMe(LivingEntity entity, double d0, boolean flag, boolean visualShape, double... checkedYs) {
2024-11-18 20:27:58 +01:00
@@ -1757,9 +2197,14 @@
2024-12-11 22:26:55 +01:00
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;
2024-11-18 20:27:58 +01:00
@@ -1830,7 +2275,7 @@
2024-12-11 22:26:55 +01:00
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));
}
2024-11-18 20:27:58 +01:00
@@ -1841,7 +2286,8 @@
2024-12-11 22:26:55 +01:00
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;
2024-11-18 20:27:58 +01:00
@@ -1884,18 +2330,170 @@
2024-12-11 22:26:55 +01:00
}
}
- 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
2024-11-11 21:35:27 +01:00
+ private EntityDamageEvent handleEntityDamage(final DamageSource damagesource, float f, final float invulnerabilityRelatedLastDamage) { // Paper - fix invulnerability reduction in EntityDamageEvent
2024-12-11 22:26:55 +01:00
+ float originalDamage = f;
2024-11-11 21:35:27 +01:00
+ // Paper start - fix invulnerability reduction in EntityDamageEvent
+ final com.google.common.base.Function<Double, Double> invulnerabilityReductionEquation = d -> {
+ if (invulnerabilityRelatedLastDamage == 0) return 0D; // no last damage, no reduction
+ // last damage existed, this means the reduction *technically* is (new damage - last damage).
+ // If the event damage was changed to something less than invul damage, hard lock it at 0.
+ if (d < invulnerabilityRelatedLastDamage) return 0D;
+ return (double) -invulnerabilityRelatedLastDamage;
+ };
+ final float originalInvulnerabilityReduction = invulnerabilityReductionEquation.apply((double) f).floatValue();
+ f += originalInvulnerabilityReduction;
+ // Paper end - fix invulnerability reduction in EntityDamageEvent
2018-09-04 01:20:03 +02:00
- amount = Math.max(amount - this.getAbsorptionAmount(), 0.0F);
- this.setAbsorptionAmount(this.getAbsorptionAmount() - (f1 - amount));
- float f2 = f1 - amount;
2024-12-11 22:26:55 +01:00
+ 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;
2024-07-08 22:01:08 +02:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2020-08-23 20:59:00 +02:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2017-01-23 00:07:56 +01:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2019-12-14 00:26:52 +01:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2024-08-26 14:01:37 +02:00
+
2024-12-11 22:26:55 +01:00
+ return -(f - Math.max(f1 / 25.0F, 0.0F));
+ }
+ return -0.0;
+ }
+ };
+ float resistanceModifier = resistance.apply((double) f).floatValue();
+ f += resistanceModifier;
2018-11-15 14:38:37 +01:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2019-08-19 16:42:35 +02:00
+
2024-12-11 22:26:55 +01:00
+ 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();
+
2024-11-11 21:35:27 +01:00
+ // Paper start - fix invulnerability reduction in EntityDamageEvent
+ return CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, freezingModifier, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, freezing, hardHat, blocking, armor, resistance, magic, absorption, (damageModifierDoubleMap, damageModifierFunctionMap) -> {
+ damageModifierFunctionMap.put(DamageModifier.INVULNERABILITY_REDUCTION, invulnerabilityReductionEquation);
+ damageModifierDoubleMap.put(DamageModifier.INVULNERABILITY_REDUCTION, (double) originalInvulnerabilityReduction);
+ });
+ // Paper end - fix invulnerability reduction in EntityDamageEvent
2024-12-11 22:26:55 +01:00
+ }
+
+ 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) {
2020-03-27 03:44:50 +01:00
+ // 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
2024-12-11 22:26:55 +01:00
+ }
2021-03-19 06:33:15 +01:00
+
2024-12-11 22:26:55 +01:00
+ // 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));
+ }
+ }
+ }
2022-08-24 15:54:11 +02:00
+
2024-12-11 22:26:55 +01:00
+ // Apply damage to helmet
+ if (damagesource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+ this.hurtHelmet(damagesource, f);
+ }
2024-11-11 21:35:27 +01:00
2024-12-11 22:26:55 +01:00
+ // 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);
+ }
2024-11-11 21:35:27 +01:00
+
2024-12-11 22:26:55 +01:00
+ // Apply blocking code // PAIL: steal from above
+ if (event.getDamage(DamageModifier.BLOCKING) < 0) {
+ this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
+ Entity entity = damagesource.getDirectEntity();
+
2024-04-27 04:08:37 +02:00
+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency
2024-12-11 22:26:55 +01:00
+ this.blockUsingShield((LivingEntity) entity);
+ }
+ }
2024-04-27 18:44:53 +02:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2022-11-22 22:16:01 +01:00
+
2024-12-11 22:26:55 +01:00
+ 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;
2024-11-18 20:27:58 +01:00
@@ -1904,13 +2502,48 @@
2024-12-11 22:26:55 +01:00
}
}
- 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) {
2023-03-16 10:04:17 +01:00
+ CriteriaTriggers.ENTITY_HURT_PLAYER.trigger((ServerPlayer) this, damagesource, originalDamage, f, true); // Paper - fix taken/dealt param order
2024-12-11 22:26:55 +01:00
+ 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) {
2023-03-16 10:04:17 +01:00
+ CriteriaTriggers.PLAYER_HURT_ENTITY.trigger((ServerPlayer) damagesource.getEntity(), this, damagesource, originalDamage, f, true); // Paper - fix taken/dealt param order
2024-12-11 22:26:55 +01:00
+ }
+
2024-06-13 17:16:01 +02:00
+ return !io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.skipVanillaDamageTickWhenShieldBlocked; // Paper - this should always return true, however expose an unsupported setting to flip this to false to enable "shield stunning".
2024-12-11 22:26:55 +01:00
+ } else {
2024-04-27 18:44:53 +02:00
+ return true; // Paper - return false ONLY if event was cancelled
2024-12-11 22:26:55 +01:00
+ }
+ // CraftBukkit end
}
}
2024-04-27 18:44:53 +02:00
+ return true; // CraftBukkit // Paper - return false ONLY if event was cancelled
2024-12-11 22:26:55 +01:00
}
public CombatTracker getCombatTracker() {
2024-11-18 20:27:58 +01:00
@@ -1935,9 +2568,19 @@
2024-12-11 22:26:55 +01:00
}
public final void setArrowCount(int stuckArrowCount) {
- this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, stuckArrowCount);
+ // CraftBukkit start
+ this.setArrowCount(stuckArrowCount, false);
2024-08-26 14:01:37 +02:00
}
2024-12-11 22:26:55 +01:00
+ 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());
2024-08-26 14:01:37 +02:00
+ }
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2024-08-26 14:01:37 +02:00
+
2024-12-11 22:26:55 +01:00
public final int getStingerCount() {
return (Integer) this.entityData.get(LivingEntity.DATA_STINGER_COUNT_ID);
2024-08-26 14:01:37 +02:00
}
2024-11-18 20:27:58 +01:00
@@ -1999,7 +2642,7 @@
2024-12-11 22:26:55 +01:00
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());
}
2024-11-18 20:27:58 +01:00
@@ -2083,7 +2726,7 @@
2024-09-29 23:20:42 +02:00
@Override
protected void onBelowWorld() {
- this.hurt(this.damageSources().fellOutOfWorld(), 4.0F);
+ this.hurt(this.damageSources().fellOutOfWorld(), this.level().getWorld().getVoidDamageAmount()); // Paper - use configured void damage amount
}
protected void updateSwingTime() {
2024-11-18 20:27:58 +01:00
@@ -2182,6 +2825,12 @@
2024-12-11 22:26:55 +01:00
public abstract ItemStack getItemBySlot(EquipmentSlot slot);
2024-11-18 20:27:58 +01:00
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start
+ public void setItemSlot(EquipmentSlot enumitemslot, ItemStack itemstack, boolean silent) {
+ this.setItemSlot(enumitemslot, itemstack);
+ }
+ // CraftBukkit end
2024-11-18 20:27:58 +01:00
+
2024-12-11 22:26:55 +01:00
public abstract void setItemSlot(EquipmentSlot slot, ItemStack stack);
2024-11-18 20:27:58 +01:00
public Iterable<ItemStack> getHandSlots() {
@@ -2292,17 +2941,29 @@
2021-11-25 10:25:09 +01:00
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));
}
2024-11-18 20:27:58 +01:00
@@ -2494,7 +3155,7 @@
2024-12-11 22:26:55 +01:00
}
- 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);
2024-11-18 20:27:58 +01:00
@@ -2507,13 +3168,13 @@
2024-12-11 22:26:55 +01:00
}
- 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();
}
2024-11-18 20:27:58 +01:00
@@ -2571,7 +3232,7 @@
2024-12-11 22:26:55 +01:00
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;
}
2024-11-18 20:27:58 +01:00
@@ -2586,7 +3247,7 @@
2024-12-11 22:26:55 +01:00
}
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() {
2024-11-18 20:27:58 +01:00
@@ -2634,7 +3295,7 @@
2024-12-11 22:26:55 +01:00
}
}
- this.detectEquipmentUpdates();
+ this.detectEquipmentUpdatesPublic(); // CraftBukkit
if (this.tickCount % 20 == 0) {
this.getCombatTracker().recheckStatus();
}
2024-11-18 20:27:58 +01:00
@@ -2687,38 +3348,16 @@
2022-08-24 15:54:11 +02:00
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;
- }
2024-11-18 20:27:58 +01:00
+ this.xRotO += Math.round((this.getXRot() - this.xRotO) / 360.0F) * 360.0F;
2022-08-24 15:54:11 +02:00
- while (this.yBodyRot - this.yBodyRotO >= 180.0F) {
- this.yBodyRotO += 360.0F;
- }
2021-09-15 20:44:22 +02:00
-
2022-08-24 15:54:11 +02:00
- while (this.getXRot() - this.xRotO < -180.0F) {
- this.xRotO -= 360.0F;
- }
2024-11-18 20:27:58 +01:00
+ this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F;
+ // Paper end
2024-11-11 21:35:27 +01:00
2022-08-24 15:54:11 +02:00
- while (this.getXRot() - this.xRotO >= 180.0F) {
- this.xRotO += 360.0F;
- }
2024-11-18 20:27:58 +01:00
-
2022-08-24 15:54:11 +02:00
- while (this.yHeadRot - this.yHeadRotO < -180.0F) {
- this.yHeadRotO -= 360.0F;
- }
2024-11-11 21:35:27 +01:00
-
2024-07-08 22:01:08 +02:00
- while (this.yHeadRot - this.yHeadRotO >= 180.0F) {
- this.yHeadRotO += 360.0F;
- }
-
2022-08-24 15:54:11 +02:00
gameprofilerfiller.pop();
this.animStep += f2;
2024-07-08 22:01:08 +02:00
if (this.isFallFlying()) {
2024-11-18 20:27:58 +01:00
@@ -2741,7 +3380,7 @@
2024-12-11 22:26:55 +01:00
this.elytraAnimationState.tick();
}
- public void detectEquipmentUpdates() {
+ public void detectEquipmentUpdatesPublic() { // CraftBukkit
Map<EquipmentSlot, ItemStack> map = this.collectEquipmentChanges();
if (map != null) {
2024-11-18 20:27:58 +01:00
@@ -2778,10 +3417,17 @@
2017-11-11 05:46:34 +01:00
throw new MatchException((String) null, (Throwable) null);
}
- ItemStack itemstack2 = itemstack1;
2024-11-18 20:27:58 +01:00
-
2021-11-25 10:25:09 +01:00
- itemstack = this.getItemBySlot(enumitemslot);
2024-11-18 20:27:58 +01:00
+ ItemStack itemstack2 = itemstack1; final ItemStack oldEquipment = itemstack2; // Paper - PlayerArmorChangeEvent - obfhelper
+
2017-11-11 05:46:34 +01:00
+ 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);
}
2024-11-18 20:27:58 +01:00
@@ -2864,7 +3510,7 @@
2021-12-01 12:36:25 +01:00
}
});
- ((ServerLevel) this.level()).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list));
+ ((ServerLevel) this.level()).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list, true)); // Paper - data sanitization
}
private ItemStack getLastArmorItem(EquipmentSlot slot) {
2024-11-18 20:27:58 +01:00
@@ -2974,8 +3620,10 @@
2020-02-09 06:26:11 +01:00
} 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;
2024-11-18 20:27:58 +01:00
@@ -3000,7 +3648,7 @@
2024-12-11 22:26:55 +01:00
{
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;
2024-11-18 20:27:58 +01:00
@@ -3017,7 +3665,7 @@
2021-12-27 02:27:43 +01:00
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()) {
2024-11-18 20:27:58 +01:00
@@ -3046,6 +3694,20 @@
2020-02-12 04:56:48 +01:00
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()) {
2024-11-18 20:27:58 +01:00
@@ -3063,6 +3725,7 @@
2024-12-11 22:26:55 +01:00
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;
}
2024-11-18 20:27:58 +01:00
@@ -3113,12 +3776,26 @@
2024-12-11 22:26:55 +01:00
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 {
2020-08-23 20:59:00 +02:00
- List list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushableBy(this));
2020-04-16 02:56:07 +02:00
+ // 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;
+ }
2020-08-23 20:59:00 +02:00
2020-04-16 02:56:07 +02:00
+ 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
2020-08-23 20:59:00 +02:00
+ List list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushable(this, this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule)); // Paper - Climbing should not bypass cramming gamerule
+
2020-04-16 02:56:07 +02:00
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;
2024-11-18 20:27:58 +01:00
@@ -3138,10 +3815,12 @@
2017-01-23 00:07:56 +01:00
}
Iterator iterator1 = list.iterator();
+ this.numCollisions = Math.max(0, this.numCollisions - this.level().paperConfig().collisions.maxEntityCollisions); // Paper - Cap entity collisions
2018-11-15 14:38:37 +01:00
- while (iterator1.hasNext()) {
2017-01-23 00:07:56 +01:00
+ while (iterator1.hasNext() && this.numCollisions < this.level().paperConfig().collisions.maxEntityCollisions) { // Paper - Cap entity collisions
2018-11-15 14:38:37 +01:00
Entity entity1 = (Entity) iterator1.next();
-
2017-01-23 00:07:56 +01:00
+ entity1.numCollisions++; // Paper - Cap entity collisions
+ this.numCollisions++; // Paper - Cap entity collisions
this.doPush(entity1);
}
}
2024-11-18 20:27:58 +01:00
@@ -3190,10 +3869,16 @@
2018-11-15 14:38:37 +01:00
@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();
2020-07-05 20:59:31 +02:00
- if (entity != null && entity != this.getVehicle() && !this.level().isClientSide) {
2018-11-15 14:38:37 +01:00
+ super.stopRiding(suppressCancellation); // Paper - Force entity dismount during teleportation
2020-07-05 20:59:31 +02:00
+ if (entity != null && entity != this.getVehicle() && !this.level().isClientSide && entity.valid) { // Paper - don't process on world gen
2018-11-15 14:38:37 +01:00
this.dismountVehicle(entity);
}
2020-07-05 20:59:31 +02:00
2024-11-18 20:27:58 +01:00
@@ -3258,7 +3943,7 @@
2022-11-22 22:16:01 +01:00
}
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);
2024-11-18 20:27:58 +01:00
@@ -3268,7 +3953,7 @@
2023-10-29 02:36:10 +01:00
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
}
}
2024-11-18 20:27:58 +01:00
@@ -3284,7 +3969,8 @@
2021-05-29 21:33:25 +02:00
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
}
}
2024-11-18 20:27:58 +01:00
@@ -3305,13 +3991,27 @@
2024-12-11 22:26:55 +01:00
@Override
public boolean isPickable() {
- return !this.isRemoved();
+ return !this.isRemoved() && this.collides; // CraftBukkit
}
2020-08-23 20:59:00 +02:00
+ // Paper start - Climbing should not bypass cramming gamerule
2024-12-11 22:26:55 +01:00
@Override
public boolean isPushable() {
- return this.isAlive() && !this.isSpectator() && !this.onClimbable();
2020-08-23 20:59:00 +02:00
+ return this.isCollidable(this.level().paperConfig().collisions.fixClimbingBypassingCrammingRule);
2024-11-18 20:27:58 +01:00
+ }
+
+ @Override
2020-08-23 20:59:00 +02:00
+ 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
2024-12-11 22:26:55 +01:00
+ public boolean canCollideWithBukkit(Entity entity) {
+ return this.isPushable() && this.collides != this.collidableExemptions.contains(entity.getUUID());
2024-11-18 20:27:58 +01:00
}
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2024-11-18 20:27:58 +01:00
@Override
2024-12-11 22:26:55 +01:00
public float getYHeadRot() {
2024-11-18 20:27:58 +01:00
@@ -3342,7 +4042,7 @@
2015-09-27 07:18:02 +02:00
}
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) {
2024-11-18 20:27:58 +01:00
@@ -3410,9 +4110,14 @@
2019-08-19 16:42:35 +02:00
}
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) {
2024-11-18 20:27:58 +01:00
@@ -3483,13 +4188,50 @@
2024-12-11 22:26:55 +01:00
this.releaseUsingItem();
} else {
if (!this.useItem.isEmpty() && this.isUsingItem()) {
- ItemStack itemstack = this.useItem.finishUsingItem(this.level(), this);
2019-08-19 16:42:35 +02:00
+ this.startUsingItem(this.getUsedItemHand(), true); // Paper - Prevent consuming the wrong itemstack
2024-12-11 22:26:55 +01:00
+ // CraftBukkit start - fire PlayerItemConsumeEvent
+ ItemStack itemstack;
2015-06-21 21:07:20 +02:00
+ PlayerItemConsumeEvent event = null; // Paper
2024-12-11 22:26:55 +01:00
+ 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);
2015-06-21 21:07:20 +02:00
+ event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem, hand); // Paper
2024-12-11 22:26:55 +01:00
+ this.level().getCraftServer().getPluginManager().callEvent(event);
2024-11-18 20:27:58 +01:00
2024-12-11 22:26:55 +01:00
+ 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();
2021-03-19 06:33:15 +01:00
+ this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use
2024-12-11 22:26:55 +01:00
+ return;
+ }
2024-11-18 20:27:58 +01:00
+
2024-12-11 22:26:55 +01:00
+ 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);
+ }
2015-06-21 21:07:20 +02:00
+ // 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
2024-12-11 22:26:55 +01:00
+ // CraftBukkit end
2024-11-11 21:35:27 +01:00
+
2024-12-11 22:26:55 +01:00
if (itemstack != this.useItem) {
this.setItemInHand(enumhand, itemstack);
}
2015-06-21 21:07:20 +02:00
this.stopUsingItem();
2019-08-19 16:42:35 +02:00
+ // Paper start
+ if (this instanceof ServerPlayer) {
2015-06-21 21:07:20 +02:00
+ ((ServerPlayer) this).getBukkitEntity().updateInventory();
+ }
+ // Paper end
}
}
2024-11-18 20:27:58 +01:00
@@ -3512,6 +4254,7 @@
2022-05-04 02:41:37 +02:00
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();
2024-11-18 20:27:58 +01:00
@@ -3544,12 +4287,69 @@
2018-06-16 08:18:16 +02:00
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;
2020-02-09 06:26:11 +01:00
}
2024-11-18 20:27:58 +01:00
+ }
+
2018-06-16 08:18:16 +02:00
+ // Paper start - Make shield blocking delay configurable
2018-09-04 01:20:03 +02:00
+ public HitResult getRayTrace(int maxDistance, ClipContext.Fluid fluidCollisionOption) {
+ if (maxDistance < 1 || maxDistance > 120) {
+ throw new IllegalArgumentException("maxDistance must be between 1-120");
2020-02-09 06:26:11 +01:00
+ }
2018-09-04 01:20:03 +02:00
+
+ 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);
2020-02-09 06:26:11 +01:00
+ }
+
2018-09-22 07:33:08 +02:00
+ 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;
+ }
+
2018-06-16 08:18:16 +02:00
+ public int shieldBlockingDelay = this.level().paperConfig().misc.shieldBlockingDelay;
+
+ public int getShieldBlockingDelay() {
+ return shieldBlockingDelay;
2024-11-18 20:27:58 +01:00
}
2018-06-16 08:18:16 +02:00
+ public void setShieldBlockingDelay(int shieldBlockingDelay) {
+ this.shieldBlockingDelay = shieldBlockingDelay;
2021-09-15 20:44:22 +02:00
+ }
2018-06-16 08:18:16 +02:00
+ // Paper end - Make shield blocking delay configurable
2021-09-15 20:44:22 +02:00
+
2018-06-16 08:18:16 +02:00
public boolean isSuppressingSlidingDownLadder() {
return this.isShiftKeyDown();
2021-09-15 20:44:22 +02:00
}
2024-11-18 20:27:58 +01:00
@@ -3568,12 +4368,18 @@
2024-12-11 22:26:55 +01:00
}
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)) {
2024-11-18 20:27:58 +01:00
@@ -3592,18 +4398,43 @@
2024-12-11 22:26:55 +01:00
}
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;
2024-11-11 21:35:27 +01:00
}
2024-12-11 22:26:55 +01:00
+ // 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);
2023-12-10 04:15:59 +01:00
+ if (!teleport.isCancelled() && teleport.getTo() != null) { // Paper
2024-12-11 22:26:55 +01:00
+ 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();
+ }
+ }
2024-11-11 21:35:27 +01:00
+ }
2024-12-11 22:26:55 +01:00
+ // 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);
}
2024-11-18 20:27:58 +01:00
@@ -3613,7 +4444,7 @@
2024-12-11 22:26:55 +01:00
entitycreature.getNavigation().stop();
}
- return true;
+ return Optional.of(true); // CraftBukkit
}
}
2024-11-18 20:27:58 +01:00
@@ -3706,7 +4537,7 @@
2024-12-11 22:26:55 +01:00
}
public void stopSleeping() {
- Optional optional = this.getSleepingPos();
+ Optional<BlockPos> optional = this.getSleepingPos(); // CraftBukkit - decompile error
Level world = this.level();
java.util.Objects.requireNonNull(world);
2024-11-18 20:27:58 +01:00
@@ -3718,9 +4549,9 @@
2024-12-11 22:26:55 +01:00
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);
2024-11-18 20:27:58 +01:00
@@ -3740,7 +4571,7 @@
2024-12-11 22:26:55 +01:00
@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;
}
2024-11-18 20:27:58 +01:00
@@ -3905,7 +4736,7 @@
2024-12-11 22:26:55 +01:00
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