Allow to change despawnInPeaceful (#12880)

This commit is contained in:
David Mayr
2025-07-26 23:13:42 +02:00
committed by GitHub
parent 9ccc51df43
commit 57c13137e4
6 changed files with 118 additions and 42 deletions

View File

@@ -1,25 +1,63 @@
package org.bukkit.entity; package org.bukkit.entity;
import com.destroystokyo.paper.entity.Pathfinder;
import io.papermc.paper.entity.Leashable;
import net.kyori.adventure.util.TriState;
import org.bukkit.Location;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.Lootable; import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.NullMarked;
import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.Nullable;
/** /**
* Represents a Mob. Mobs are living entities with simple AI. * Represents a Mob. Mobs are living entities with simple AI.
*/ */
public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Leashable { // Paper - Leashable API @NullMarked
public interface Mob extends LivingEntity, Lootable, Leashable {
/**
* Check if a mob should be despawned when the world is set to peaceful difficulty.
* This also takes the {@link Mob#getDespawnInPeacefulOverride()} into account.
*
* @return True if the entity should be removed in peaceful
*/
boolean shouldDespawnInPeaceful();
/**
* Sets if the entity should be despawned when the game is set to peaceful difficulty.
* <ul>
* <li>{@link TriState#NOT_SET} Use the default behavior for the entity</li>
* <li>{@link TriState#TRUE} The entity will be removed in Peaceful difficulty</li>
* <li>{@link TriState#FALSE} The entity will not automatically be removed in Peaceful difficulty</li>
* </ul>
*
* @param state a TriState representing the state of the override
*/
void setDespawnInPeacefulOverride(TriState state);
/**
* Gets the current override value for whether this entity should despawn in peaceful difficulty.
* <ul>
* <li>{@link TriState#NOT_SET} Use the default behavior for the entity</li>
* <li>{@link TriState#TRUE} The entity will be removed in Peaceful difficulty</li>
* <li>{@link TriState#FALSE} The entity will not automatically be removed in Peaceful difficulty</li>
* </ul>
*
* @return a TriState representing the state of the override
* @see Mob#setDespawnInPeacefulOverride(TriState)
*/
TriState getDespawnInPeacefulOverride();
// Paper start
@Override @Override
org.bukkit.inventory.@org.jetbrains.annotations.NotNull EntityEquipment getEquipment(); EntityEquipment getEquipment();
/** /**
* Enables access to control the pathing of an Entity * Enables access to control the pathing of an Entity
* @return Pathfinding Manager for this entity * @return Pathfinding Manager for this entity
*/ */
@NotNull Pathfinder getPathfinder();
com.destroystokyo.paper.entity.Pathfinder getPathfinder();
/** /**
* Check if this mob is exposed to daylight * Check if this mob is exposed to daylight
@@ -35,7 +73,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* *
* @param location location to look at * @param location location to look at
*/ */
void lookAt(@NotNull org.bukkit.Location location); void lookAt(Location location);
/** /**
* Instruct this Mob to look at a specific Location * Instruct this Mob to look at a specific Location
@@ -46,7 +84,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* @param headRotationSpeed head rotation speed * @param headRotationSpeed head rotation speed
* @param maxHeadPitch max head pitch rotation * @param maxHeadPitch max head pitch rotation
*/ */
void lookAt(@NotNull org.bukkit.Location location, float headRotationSpeed, float maxHeadPitch); void lookAt(Location location, float headRotationSpeed, float maxHeadPitch);
/** /**
* Instruct this Mob to look at a specific Entity * Instruct this Mob to look at a specific Entity
@@ -57,7 +95,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* *
* @param entity entity to look at * @param entity entity to look at
*/ */
void lookAt(@NotNull Entity entity); void lookAt(Entity entity);
/** /**
* Instruct this Mob to look at a specific Entity * Instruct this Mob to look at a specific Entity
@@ -70,7 +108,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* @param headRotationSpeed head rotation speed * @param headRotationSpeed head rotation speed
* @param maxHeadPitch max head pitch rotation * @param maxHeadPitch max head pitch rotation
*/ */
void lookAt(@NotNull Entity entity, float headRotationSpeed, float maxHeadPitch); void lookAt(Entity entity, float headRotationSpeed, float maxHeadPitch);
/** /**
* Instruct this Mob to look at a specific position * Instruct this Mob to look at a specific position
@@ -109,7 +147,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* @return the max head pitch rotation * @return the max head pitch rotation
*/ */
int getMaxHeadPitch(); int getMaxHeadPitch();
// Paper end
/** /**
* Instructs this Mob to set the specified LivingEntity as its target. * Instructs this Mob to set the specified LivingEntity as its target.
* <p> * <p>
@@ -118,26 +156,25 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* *
* @param target New LivingEntity to target, or null to clear the target * @param target New LivingEntity to target, or null to clear the target
*/ */
public void setTarget(@Nullable LivingEntity target); void setTarget(@Nullable LivingEntity target);
/** /**
* Gets the current target of this Mob * Gets the current target of this Mob
* *
* @return Current target of this creature, or null if none exists * @return Current target of this creature, or null if none exists
*/ */
@Nullable @Nullable LivingEntity getTarget();
public LivingEntity getTarget();
/** /**
* Sets whether this mob is aware of its surroundings. * Sets whether this mob is aware of its surroundings.
* * <p>
* Unaware mobs will still move if pushed, attacked, etc. but will not move * Unaware mobs will still move if pushed, attacked, etc. but will not move
* or perform any actions on their own. Unaware mobs may also have other * or perform any actions on their own. Unaware mobs may also have other
* unspecified behaviours disabled, such as drowning. * unspecified behaviours disabled, such as drowning.
* *
* @param aware whether the mob is aware * @param aware whether the mob is aware
*/ */
public void setAware(boolean aware); void setAware(boolean aware);
/** /**
* Gets whether this mob is aware of its surroundings. * Gets whether this mob is aware of its surroundings.
@@ -148,7 +185,7 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* *
* @return whether the mob is aware * @return whether the mob is aware
*/ */
public boolean isAware(); boolean isAware();
/** /**
* Get the {@link Sound} this mob makes while ambiently existing. This sound * Get the {@link Sound} this mob makes while ambiently existing. This sound
@@ -160,18 +197,14 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* *
* @return the ambient sound, or null if this entity is ambiently quiet * @return the ambient sound, or null if this entity is ambiently quiet
*/ */
@Nullable @Nullable Sound getAmbientSound();
public Sound getAmbientSound();
// Paper start - LootTable API
@Override @Override
default void setLootTable(final @Nullable org.bukkit.loot.LootTable table, final long seed) { default void setLootTable(final @Nullable LootTable table, final long seed) {
this.setLootTable(table); this.setLootTable(table);
this.setSeed(seed); this.setSeed(seed);
} }
// Paper end - LootTable API
// Paper start - Missing Entity API
/** /**
* Some mobs will raise their arm(s) when aggressive: * Some mobs will raise their arm(s) when aggressive:
* <ul> * <ul>
@@ -203,30 +236,25 @@ public interface Mob extends LivingEntity, Lootable, io.papermc.paper.entity.Lea
* @see #isAggressive() * @see #isAggressive()
*/ */
void setAggressive(boolean aggressive); void setAggressive(boolean aggressive);
// Paper end - Missing Entity API
// Paper start - left-handed API
/** /**
* Check if Mob is left-handed * Check if Mob is left-handed
* *
* @return True if left-handed * @return True if left-handed
*/ */
public boolean isLeftHanded(); boolean isLeftHanded();
/** /**
* Set if Mob is left-handed * Set if Mob is left-handed
* *
* @param leftHanded True if left-handed * @param leftHanded True if left-handed
*/ */
public void setLeftHanded(boolean leftHanded); void setLeftHanded(boolean leftHanded);
// Paper end - left-handed API
// Paper start - mob xp reward API
/** /**
* Gets the amount of experience the mob will possibly drop. This value is randomized and it can give different results * Gets the amount of experience the mob will possibly drop. This value is randomized and it can give different results
* *
* @return the amount of experience the mob will possibly drop * @return the amount of experience the mob will possibly drop
*/ */
public int getPossibleExperienceReward(); int getPossibleExperienceReward();
// Paper end - mob xp reward API
} }

View File

@@ -484,7 +484,7 @@ index c70a58f5f633fa8e255f74c42f5e87c96b7b013a..ec20a5a6d7c8f65abda528fec36bec7b
public void tick() { public void tick() {
super.tick(); super.tick();
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 75f81a6bc156a6455a616b8de0d7701fd2255a2d..b3d951670b3a097d04cfe347d7df496b1d0a0e09 100644 index b9f0745cdc42a093b69f46e353b99adf0c5aac56..a5d65e1b31e2e43cf039b8a19286a5324a739bbe 100644
--- a/net/minecraft/world/entity/Entity.java --- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java
@@ -409,6 +409,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @@ -409,6 +409,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -543,7 +543,7 @@ index 75f81a6bc156a6455a616b8de0d7701fd2255a2d..b3d951670b3a097d04cfe347d7df496b
movement = this.maybeBackOffFromEdge(movement, type); movement = this.maybeBackOffFromEdge(movement, type);
Vec3 vec3 = this.collide(movement); Vec3 vec3 = this.collide(movement);
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 13cf40918ead3e2cb2699398ac659a3e1806294b..1ba342a1a60951f828034d3ed535b577b3990bf6 100644 index 8dd8a3d2a862998a920f226b2a6d9a877aac70a8..0c52e315557e1dae6a852694786e72241fff1e29 100644
--- a/net/minecraft/world/entity/LivingEntity.java --- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java +++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3215,6 +3215,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin @@ -3215,6 +3215,14 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -562,10 +562,10 @@ index 13cf40918ead3e2cb2699398ac659a3e1806294b..1ba342a1a60951f828034d3ed535b577
public void tick() { public void tick() {
super.tick(); super.tick();
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index 3047763580fabd069db5e90a9a854396c6243763..0470c4bbf8be7e48ce8dfa4910c3b9f5ebb23360 100644 index c737fd87e804129fac95be7d19b4aafab38d8b94..21c6d4746c6a905ec312dc1e35535cbd13868322 100644
--- a/net/minecraft/world/entity/Mob.java --- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java
@@ -206,6 +206,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -207,6 +207,19 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
return this.lookControl; return this.lookControl;
} }

View File

@@ -23,11 +23,12 @@
public GoalSelector targetSelector; public GoalSelector targetSelector;
@Nullable @Nullable
private LivingEntity target; private LivingEntity target;
@@ -129,6 +_,7 @@ @@ -129,6 +_,8 @@
private Leashable.LeashData leashData; private Leashable.LeashData leashData;
private BlockPos homePosition = BlockPos.ZERO; private BlockPos homePosition = BlockPos.ZERO;
private int homeRadius = -1; private int homeRadius = -1;
+ public boolean aware = true; // CraftBukkit + public boolean aware = true; // CraftBukkit
+ public net.kyori.adventure.util.TriState despawnInPeacefulOverride = net.kyori.adventure.util.TriState.NOT_SET; // Paper - allow changing despawnInPeaceful
protected Mob(EntityType<? extends Mob> entityType, Level level) { protected Mob(EntityType<? extends Mob> entityType, Level level) {
super(entityType, level); super(entityType, level);
@@ -84,11 +85,16 @@
} }
@Override @Override
@@ -365,13 +_,22 @@ @@ -365,13 +_,27 @@
if (this.isNoAi()) { if (this.isNoAi()) {
output.putBoolean("NoAI", this.isNoAi()); output.putBoolean("NoAI", this.isNoAi());
} }
+ output.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit + output.putBoolean("Bukkit.Aware", this.aware); // CraftBukkit
+ // Paper start - allow changing despawnInPeaceful
+ if (this.despawnInPeacefulOverride != net.kyori.adventure.util.TriState.NOT_SET) {
+ output.putString("Paper.DespawnInPeacefulOverride", this.despawnInPeacefulOverride.name());
+ }
+ // Paper end - allow changing despawnInPeaceful
} }
@Override @Override
@@ -109,11 +115,12 @@
this.dropChances = input.read("drop_chances", DropChances.CODEC).orElse(DropChances.DEFAULT); this.dropChances = input.read("drop_chances", DropChances.CODEC).orElse(DropChances.DEFAULT);
this.readLeashData(input); this.readLeashData(input);
this.homeRadius = input.getIntOr("home_radius", -1); this.homeRadius = input.getIntOr("home_radius", -1);
@@ -383,6 +_,7 @@ @@ -383,6 +_,8 @@
this.lootTable = input.read("DeathLootTable", LootTable.KEY_CODEC); this.lootTable = input.read("DeathLootTable", LootTable.KEY_CODEC);
this.lootTableSeed = input.getLongOr("DeathLootTableSeed", 0L); this.lootTableSeed = input.getLongOr("DeathLootTableSeed", 0L);
this.setNoAi(input.getBooleanOr("NoAI", false)); this.setNoAi(input.getBooleanOr("NoAI", false));
+ this.aware = input.getBooleanOr("Bukkit.Aware", true); // CraftBukkit + this.aware = input.getBooleanOr("Bukkit.Aware", true); // CraftBukkit
+ this.despawnInPeacefulOverride = input.read("Paper.DespawnInPeacefulOverride", io.papermc.paper.util.PaperCodecs.TRI_STATE_CODEC).orElse(net.kyori.adventure.util.TriState.NOT_SET); // Paper - allow changing despawnInPeaceful
} }
@Override @Override
@@ -176,11 +183,25 @@
} }
ItemStack itemStack = equipmentSlotForItem.limit(stack); ItemStack itemStack = equipmentSlotForItem.limit(stack);
@@ -608,22 +_,29 @@ @@ -601,29 +_,42 @@
return this.isPassenger();
}
+ // Paper start - allow changing despawnInPeaceful
+ public final boolean shouldActuallyDespawnInPeaceful() {
+ return this.despawnInPeacefulOverride.toBooleanOrElse(this.shouldDespawnInPeaceful());
+ }
+ // Paper end - allow changing despawnInPeaceful
+
protected boolean shouldDespawnInPeaceful() {
return false;
}
@Override @Override
public void checkDespawn() { public void checkDespawn() {
if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { - if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
- this.discard(); - this.discard();
+ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldActuallyDespawnInPeaceful()) { //Paper - allow changing despawnInPeaceful
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) {
- Entity nearestPlayer = this.level().getNearestPlayer(this, -1.0); - Entity nearestPlayer = this.level().getNearestPlayer(this, -1.0);

View File

@@ -95,8 +95,9 @@
@Override @Override
public void checkDespawn() { public void checkDespawn() {
if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { - if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) {
- this.discard(); - this.discard();
+ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldActuallyDespawnInPeaceful()) { //Paper - allow changing despawnInPeaceful
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else { } else {
this.noActionTime = 0; this.noActionTime = 0;

View File

@@ -15,6 +15,7 @@ import io.papermc.paper.registry.TypedKey;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.util.TriState;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps; import net.minecraft.resources.RegistryOps;
import org.bukkit.Keyed; import org.bukkit.Keyed;
@@ -24,6 +25,14 @@ import org.jspecify.annotations.NullMarked;
@NullMarked @NullMarked
public final class PaperCodecs { public final class PaperCodecs {
public static final Codec<TriState> TRI_STATE_CODEC = Codec.STRING.comapFlatMap(s -> {
try {
return DataResult.success(TriState.valueOf(s));
} catch (Exception e) {
return DataResult.error(() -> "Unknown TriState value: " + s);
}
}, TriState::name);
/** /**
* This codec is lenient on decoding and encoding compared to native OptionalFieldCodec * This codec is lenient on decoding and encoding compared to native OptionalFieldCodec
* which only has options to be lenient on decoding. * which only has options to be lenient on decoding.

View File

@@ -2,6 +2,7 @@ package org.bukkit.craftbukkit.entity;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.util.Optional; import java.util.Optional;
import net.kyori.adventure.util.TriState;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.craftbukkit.CraftLootTable; import org.bukkit.craftbukkit.CraftLootTable;
@@ -20,6 +21,22 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob, io.pape
this.paperPathfinder = new com.destroystokyo.paper.entity.PaperPathfinder(entity); // Paper - Mob Pathfinding API this.paperPathfinder = new com.destroystokyo.paper.entity.PaperPathfinder(entity); // Paper - Mob Pathfinding API
} }
@Override
public boolean shouldDespawnInPeaceful() {
return this.getHandle().shouldActuallyDespawnInPeaceful();
}
@Override
public void setDespawnInPeacefulOverride(final TriState state) {
Preconditions.checkArgument(state != null, "TriState cannot be null");
this.getHandle().despawnInPeacefulOverride = state;
}
@Override
public TriState getDespawnInPeacefulOverride() {
return this.getHandle().despawnInPeacefulOverride;
}
@Override @Override
public net.minecraft.world.entity.Mob getHandle() { public net.minecraft.world.entity.Mob getHandle() {
return (net.minecraft.world.entity.Mob) this.entity; return (net.minecraft.world.entity.Mob) this.entity;