PaperMC/paper-server/patches/sources/net/minecraft/world/entity/Mob.java.patch
Phoenix616 04afedcccf Improve death events
This adds the ability to cancel the death events and to modify the sound
an entity makes when dying. (In cases were no sound should it will be
called with shouldPlaySound set to false allowing unsilencing of silent
entities)

It makes handling of entity deaths a lot nicer as you no longer need
to listen on the damage event and calculate if the entity dies yourself
to cancel the death which has the benefit of also receiving the dropped
items and experience which is otherwise only properly possible by using
internal code.

== AT ==
public net.minecraft.world.entity.LivingEntity getDeathSound()Lnet/minecraft/sounds/SoundEvent;
public net.minecraft.world.entity.LivingEntity getSoundVolume()F
2018-08-21 01:39:35 +01:00

424 lines
20 KiB
Diff

--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -84,6 +84,17 @@
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.AABB;
+// CraftBukkit start
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.entity.CraftLivingEntity;
+import org.bukkit.event.entity.CreatureSpawnEvent;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
+import org.bukkit.event.entity.EntityTargetEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+import org.bukkit.event.entity.EntityUnleashEvent;
+import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason;
+// CraftBukkit end
public abstract class Mob extends LivingEntity implements EquipmentUser, Leashable, Targeting {
@@ -112,6 +123,7 @@
private final BodyRotationControl bodyRotationControl;
protected PathNavigation navigation;
public GoalSelector goalSelector;
+ @Nullable public net.minecraft.world.entity.ai.goal.FloatGoal goalFloat; // Paper - Allow nerfed mobs to jump and float
public GoalSelector targetSelector;
@Nullable
private LivingEntity target;
@@ -132,6 +144,8 @@
private BlockPos restrictCenter;
private float restrictRadius;
+ public boolean aware = true; // CraftBukkit
+
protected Mob(EntityType<? extends Mob> type, Level world) {
super(type, world);
this.handItems = NonNullList.withSize(2, ItemStack.EMPTY);
@@ -160,6 +174,12 @@
}
+ // CraftBukkit start
+ public void setPersistenceRequired(boolean persistenceRequired) {
+ this.persistenceRequired = persistenceRequired;
+ }
+ // CraftBukkit end
+
protected void registerGoals() {}
public static AttributeSupplier.Builder createMobAttributes() {
@@ -264,11 +284,42 @@
@Nullable
protected final LivingEntity getTargetFromBrain() {
- return (LivingEntity) this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse((Object) null);
+ return (LivingEntity) this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null); // CraftBukkit - decompile error
}
public void setTarget(@Nullable LivingEntity target) {
- this.target = target;
+ // CraftBukkit start - fire event
+ this.setTarget(target, EntityTargetEvent.TargetReason.UNKNOWN, true);
+ }
+
+ public boolean setTarget(LivingEntity entityliving, EntityTargetEvent.TargetReason reason, boolean fireEvent) {
+ if (this.getTarget() == entityliving) return false;
+ if (fireEvent) {
+ if (reason == EntityTargetEvent.TargetReason.UNKNOWN && this.getTarget() != null && entityliving == null) {
+ reason = this.getTarget().isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED;
+ }
+ if (reason == EntityTargetEvent.TargetReason.UNKNOWN) {
+ this.level().getCraftServer().getLogger().log(java.util.logging.Level.WARNING, "Unknown target reason, please report on the issue tracker", new Exception());
+ }
+ CraftLivingEntity ctarget = null;
+ if (entityliving != null) {
+ ctarget = (CraftLivingEntity) entityliving.getBukkitEntity();
+ }
+ EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(this.getBukkitEntity(), ctarget, reason);
+ this.level().getCraftServer().getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ return false;
+ }
+
+ if (event.getTarget() != null) {
+ entityliving = ((CraftLivingEntity) event.getTarget()).getHandle();
+ } else {
+ entityliving = null;
+ }
+ }
+ this.target = entityliving;
+ return true;
+ // CraftBukkit end
}
@Override
@@ -399,6 +450,12 @@
return null;
}
+ // CraftBukkit start - Add delegate method
+ public SoundEvent getAmbientSound0() {
+ return this.getAmbientSound();
+ }
+ // CraftBukkit end
+
@Override
public void addAdditionalSaveData(CompoundTag nbt) {
super.addAdditionalSaveData(nbt);
@@ -473,13 +530,25 @@
nbt.putBoolean("NoAI", this.isNoAi());
}
+ nbt.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit
}
@Override
public void readAdditionalSaveData(CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
- this.setCanPickUpLoot(nbt.getBoolean("CanPickUpLoot"));
- this.persistenceRequired = nbt.getBoolean("PersistenceRequired");
+ // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it
+ if (nbt.contains("CanPickUpLoot", 99)) {
+ boolean data = nbt.getBoolean("CanPickUpLoot");
+ if (isLevelAtLeast(nbt, 1) || data) {
+ this.setCanPickUpLoot(data);
+ }
+ }
+
+ boolean data = nbt.getBoolean("PersistenceRequired");
+ if (isLevelAtLeast(nbt, 1) || data) {
+ this.persistenceRequired = data;
+ }
+ // CraftBukkit end
ListTag nbttaglist;
CompoundTag nbttagcompound1;
int i;
@@ -547,6 +616,11 @@
this.lootTableSeed = nbt.getLong("DeathLootTableSeed");
this.setNoAi(nbt.getBoolean("NoAI"));
+ // CraftBukkit start
+ if (nbt.contains("Bukkit.Aware")) {
+ this.aware = nbt.getBoolean("Bukkit.Aware");
+ }
+ // CraftBukkit end
}
@Override
@@ -608,6 +682,11 @@
ItemEntity entityitem = (ItemEntity) iterator.next();
if (!entityitem.isRemoved() && !entityitem.getItem().isEmpty() && !entityitem.hasPickUpDelay() && this.wantsToPickUp(worldserver, entityitem.getItem())) {
+ // Paper start - Item#canEntityPickup
+ if (!entityitem.canMobPickup) {
+ continue;
+ }
+ // Paper end - Item#canEntityPickup
this.pickUpItem(worldserver, entityitem);
}
}
@@ -623,23 +702,29 @@
protected void pickUpItem(ServerLevel world, ItemEntity itemEntity) {
ItemStack itemstack = itemEntity.getItem();
- ItemStack itemstack1 = this.equipItemIfPossible(world, itemstack.copy());
+ ItemStack itemstack1 = this.equipItemIfPossible(world, itemstack.copy(), itemEntity); // CraftBukkit - add item
if (!itemstack1.isEmpty()) {
this.onItemPickup(itemEntity);
this.take(itemEntity, itemstack1.getCount());
itemstack.shrink(itemstack1.getCount());
if (itemstack.isEmpty()) {
- itemEntity.discard();
+ itemEntity.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
}
}
}
public ItemStack equipItemIfPossible(ServerLevel world, ItemStack stack) {
- EquipmentSlot enumitemslot = this.getEquipmentSlotForItem(stack);
+ // CraftBukkit start - add item
+ return this.equipItemIfPossible(world, stack, null);
+ }
+
+ public ItemStack equipItemIfPossible(ServerLevel worldserver, ItemStack itemstack, ItemEntity entityitem) {
+ // CraftBukkit end
+ EquipmentSlot enumitemslot = this.getEquipmentSlotForItem(itemstack);
ItemStack itemstack1 = this.getItemBySlot(enumitemslot);
- boolean flag = this.canReplaceCurrentItem(stack, itemstack1, enumitemslot);
+ boolean flag = this.canReplaceCurrentItem(itemstack, itemstack1, enumitemslot);
if (enumitemslot.isArmor() && !flag) {
enumitemslot = EquipmentSlot.MAINHAND;
@@ -647,14 +732,22 @@
flag = itemstack1.isEmpty();
}
- if (flag && this.canHoldItem(stack)) {
+ // CraftBukkit start
+ boolean canPickup = flag && this.canHoldItem(itemstack);
+ if (entityitem != null) {
+ canPickup = !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entityitem, 0, !canPickup).isCancelled();
+ }
+ if (canPickup) {
+ // CraftBukkit end
double d0 = (double) this.getEquipmentDropChance(enumitemslot);
if (!itemstack1.isEmpty() && (double) Math.max(this.random.nextFloat() - 0.1F, 0.0F) < d0) {
- this.spawnAtLocation(world, itemstack1);
+ this.forceDrops = true; // CraftBukkit
+ this.spawnAtLocation(worldserver, itemstack1);
+ this.forceDrops = false; // CraftBukkit
}
- ItemStack itemstack2 = enumitemslot.limit(stack);
+ ItemStack itemstack2 = enumitemslot.limit(itemstack);
this.setItemSlotAndDropWhenKilled(enumitemslot, itemstack2);
return itemstack2;
@@ -768,25 +861,29 @@
@Override
public void checkDespawn() {
if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
- Player entityhuman = this.level().getNearestPlayer(this, -1.0D);
+ Player entityhuman = this.level().findNearbyPlayer(this, -1.0D, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper - Affects Spawning API
if (entityhuman != null) {
- double d0 = entityhuman.distanceToSqr((Entity) this);
- int i = this.getType().getCategory().getDespawnDistance();
- int j = i * i;
-
- if (d0 > (double) j && this.removeWhenFarAway(d0)) {
- this.discard();
+ // Paper start - Configurable despawn distances
+ final io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DespawnRangePair despawnRangePair = this.level().paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory());
+ final io.papermc.paper.configuration.type.DespawnRange.Shape shape = this.level().paperConfig().entities.spawning.despawnRangeShape;
+ final double dy = Math.abs(entityhuman.getY() - this.getY());
+ final double dySqr = Math.pow(dy, 2);
+ final double dxSqr = Math.pow(entityhuman.getX() - this.getX(), 2);
+ final double dzSqr = Math.pow(entityhuman.getZ() - this.getZ(), 2);
+ final double distanceSquared = dxSqr + dzSqr + dySqr;
+ // Despawn if hard/soft limit is exceeded
+ if (despawnRangePair.hard().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy) && this.removeWhenFarAway(distanceSquared)) {
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
}
-
- int k = this.getType().getCategory().getNoDespawnDistance();
- int l = k * k;
-
- if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && d0 > (double) l && this.removeWhenFarAway(d0)) {
- this.discard();
- } else if (d0 < (double) l) {
+ if (despawnRangePair.soft().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy)) {
+ if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && this.removeWhenFarAway(distanceSquared)) {
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ } else {
+ // Paper end - Configurable despawn distances
this.noActionTime = 0;
}
}
@@ -799,6 +896,15 @@
@Override
protected final void serverAiStep() {
++this.noActionTime;
+ // Paper start - Allow nerfed mobs to jump and float
+ if (!this.aware) {
+ if (goalFloat != null) {
+ if (goalFloat.canUse()) goalFloat.tick();
+ this.getJumpControl().tick();
+ }
+ return;
+ }
+ // Paper end - Allow nerfed mobs to jump and float
ProfilerFiller gameprofilerfiller = Profiler.get();
gameprofilerfiller.push("sensing");
@@ -1011,6 +1117,12 @@
}
+ // Paper start
+ protected boolean shouldSkipLoot(EquipmentSlot slot) { // method to avoid to fallback into the global mob loot logic (i.e fox)
+ return false;
+ }
+ // Paper end
+
@Override
protected void dropCustomDeathLoot(ServerLevel world, DamageSource source, boolean causedByPlayer) {
super.dropCustomDeathLoot(world, source, causedByPlayer);
@@ -1018,6 +1130,7 @@
while (iterator.hasNext()) {
EquipmentSlot enumitemslot = (EquipmentSlot) iterator.next();
+ if (this.shouldSkipLoot(enumitemslot)) continue; // Paper
ItemStack itemstack = this.getItemBySlot(enumitemslot);
float f = this.getEquipmentDropChance(enumitemslot);
@@ -1042,7 +1155,13 @@
}
this.spawnAtLocation(world, itemstack);
+ if (this.clearEquipmentSlots) { // Paper
this.setItemSlot(enumitemslot, ItemStack.EMPTY);
+ // Paper start
+ } else {
+ this.clearedEquipmentSlots.add(enumitemslot);
+ }
+ // Paper end
}
}
}
@@ -1338,7 +1457,7 @@
if (itemstack.getItem() instanceof SpawnEggItem) {
if (this.level() instanceof ServerLevel) {
SpawnEggItem itemmonsteregg = (SpawnEggItem) itemstack.getItem();
- Optional<Mob> optional = itemmonsteregg.spawnOffspringFromSpawnEgg(player, this, this.getType(), (ServerLevel) this.level(), this.position(), itemstack);
+ Optional<Mob> optional = itemmonsteregg.spawnOffspringFromSpawnEgg(player, this, (EntityType<? extends Mob>) this.getType(), (ServerLevel) this.level(), this.position(), itemstack); // CraftBukkit - decompile error
optional.ifPresent((entityinsentient) -> {
this.onOffspringSpawnedFromEgg(player, entityinsentient);
@@ -1389,28 +1508,51 @@
return this.restrictRadius != -1.0F;
}
+ // CraftBukkit start
@Nullable
public <T extends Mob> T convertTo(EntityType<T> entityType, ConversionParams context, EntitySpawnReason reason, ConversionParams.AfterConversion<T> finalizer) {
+ return this.convertTo(entityType, context, reason, finalizer, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT);
+ }
+
+ @Nullable
+ public <T extends Mob> T convertTo(EntityType<T> entitytypes, ConversionParams conversionparams, EntitySpawnReason entityspawnreason, ConversionParams.AfterConversion<T> conversionparams_a, EntityTransformEvent.TransformReason transformReason, CreatureSpawnEvent.SpawnReason spawnReason) {
+ // Paper start - entity zap event - allow cancellation of conversion post creation
+ return this.convertTo(entitytypes, conversionparams, entityspawnreason, e -> { conversionparams_a.finalizeConversion(e); return true; }, transformReason, spawnReason);
+ }
+ @Nullable
+ public <T extends Mob> T convertTo(EntityType<T> entitytypes, ConversionParams conversionparams, EntitySpawnReason entityspawnreason, ConversionParams.CancellingAfterConversion<T> conversionparams_a, EntityTransformEvent.TransformReason transformReason, CreatureSpawnEvent.SpawnReason spawnReason) {
+ // Paper end - entity zap event - allow cancellation of conversion post creation
+ // CraftBukkit end
if (this.isRemoved()) {
return null;
} else {
- T t0 = (Mob) entityType.create(this.level(), reason);
+ T t0 = entitytypes.create(this.level(), EntitySpawnReason.CONVERSION); // CraftBukkit - decompile error
if (t0 == null) {
return null;
} else {
- context.type().convert(this, t0, context);
- finalizer.finalizeConversion(t0);
+ conversionparams.type().convert(this, t0, conversionparams);
+ if (!conversionparams_a.finalizeConversionOrCancel(t0)) return null; // Paper - entity zap event - return null if conversion was cancelled
Level world = this.level();
+
+ // CraftBukkit start
+ if (transformReason == null) {
+ // Special handling for slime split and pig lightning
+ return t0;
+ }
+ if (CraftEventFactory.callEntityTransformEvent(this, t0, transformReason).isCancelled()) {
+ return null;
+ }
+ // CraftBukkit end
if (world instanceof ServerLevel) {
ServerLevel worldserver = (ServerLevel) world;
- worldserver.addFreshEntity(t0);
+ worldserver.addFreshEntity(t0, spawnReason); // CraftBukkit
}
- if (context.type().shouldDiscardAfterConversion()) {
- this.discard();
+ if (conversionparams.type().shouldDiscardAfterConversion()) {
+ this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause
}
return t0;
@@ -1420,10 +1562,22 @@
@Nullable
public <T extends Mob> T convertTo(EntityType<T> entityType, ConversionParams context, ConversionParams.AfterConversion<T> finalizer) {
- return this.convertTo(entityType, context, EntitySpawnReason.CONVERSION, finalizer);
+ // CraftBukkit start
+ return this.convertTo(entityType, context, finalizer, EntityTransformEvent.TransformReason.UNKNOWN, CreatureSpawnEvent.SpawnReason.DEFAULT);
}
@Nullable
+ public <T extends Mob> T convertTo(EntityType<T> entitytypes, ConversionParams conversionparams, ConversionParams.AfterConversion<T> conversionparams_a, EntityTransformEvent.TransformReason transformReason, CreatureSpawnEvent.SpawnReason spawnReason) {
+ // Paper start - entity zap event - allow cancellation of conversion post creation
+ return this.convertTo(entitytypes, conversionparams, e -> { conversionparams_a.finalizeConversion(e); return true; }, transformReason, spawnReason);
+ }
+ public <T extends Mob> T convertTo(EntityType<T> entitytypes, ConversionParams conversionparams, ConversionParams.CancellingAfterConversion<T> conversionparams_a, EntityTransformEvent.TransformReason transformReason, CreatureSpawnEvent.SpawnReason spawnReason) {
+ // Paper start - entity zap event - allow cancellation of conversion post creation
+ return this.convertTo(entitytypes, conversionparams, EntitySpawnReason.CONVERSION, conversionparams_a, transformReason, spawnReason);
+ // CraftBukkit end
+ }
+
+ @Nullable
@Override
public Leashable.LeashData getLeashData() {
return this.leashData;
@@ -1458,6 +1612,7 @@
boolean flag1 = super.startRiding(entity, force);
if (flag1 && this.isLeashed()) {
+ this.level().getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit
this.dropLeash();
}
@@ -1542,7 +1697,7 @@
if (f1 > 0.0F && target instanceof LivingEntity) {
entityliving = (LivingEntity) target;
- entityliving.knockback((double) (f1 * 0.5F), (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)));
+ entityliving.knockback((double) (f1 * 0.5F), (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
this.setDeltaMovement(this.getDeltaMovement().multiply(0.6D, 1.0D, 0.6D));
}