diff --git a/paper-api/src/main/java/org/bukkit/entity/Entity.java b/paper-api/src/main/java/org/bukkit/entity/Entity.java index dc91a3cf3b..e99cacc078 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Entity.java +++ b/paper-api/src/main/java/org/bukkit/entity/Entity.java @@ -5,6 +5,7 @@ import java.util.Set; import java.util.UUID; import io.papermc.paper.datacomponent.DataComponentView; import io.papermc.paper.entity.LookAnchor; +import net.kyori.adventure.util.TriState; import org.bukkit.Chunk; // Paper import org.bukkit.EntityEffect; import org.bukkit.Location; @@ -296,17 +297,43 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent /** * Sets if the entity has visual fire (it will always appear to be on fire). * + * @deprecated This method doesn't allow visually extinguishing a burning entity, + * use {@link #setVisualFire(TriState)} instead * @param fire whether visual fire is enabled */ + @Deprecated void setVisualFire(boolean fire); + /** + * Sets if the entity has visual fire (it will always appear to be on fire). + * + * + * @param fire a TriState value representing the state of the visual fire. + */ + void setVisualFire(@NotNull TriState fire); + /** * Gets if the entity has visual fire (it will always appear to be on fire). * + * @deprecated This method can't properly reflect the three possible states of visual fire, + * use {@link #getVisualFire()} instead * @return whether visual fire is enabled */ + @Deprecated boolean isVisualFire(); + /** + * Retrieves the visual fire state of the object. + * + * @return A TriState indicating the current visual fire state. + */ + @NotNull + TriState getVisualFire(); + /** * Returns the entity's current freeze ticks (amount of ticks the entity has * been in powdered snow). diff --git a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch index 29e206088b..23184702aa 100644 --- a/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0015-Moonrise-optimisation-patches.patch @@ -28597,7 +28597,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896 + // Paper end - block counting } diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080df422be7 100644 +index 663fb13233afb51f935c30ac2acae808809754c6..81a18b8e605bd4c28b48a32c80be231609182970 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -140,7 +140,7 @@ import net.minecraft.world.scores.ScoreHolder; @@ -28950,7 +28950,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 } private static float[] collectCandidateStepUpHeights(AABB box, List colliders, float deltaY, float maxUpStep) { -@@ -2600,21 +2748,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -2616,21 +2764,110 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public boolean isInWall() { @@ -29072,7 +29072,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 } public InteractionResult interact(Player player, InteractionHand hand) { -@@ -4062,15 +4299,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4078,15 +4315,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public Iterable getIndirectPassengers() { @@ -29098,7 +29098,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 } public int countPlayerPassengers() { -@@ -4213,77 +4452,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4229,77 +4468,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return Mth.lerp(partialTick, this.yRotO, this.yRot); } @@ -29289,7 +29289,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 public boolean touchingUnloadedChunk() { AABB aabb = this.getBoundingBox().inflate(1.0); -@@ -4438,6 +4736,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4454,6 +4752,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { @@ -29305,7 +29305,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 if (!checkPosition(this, x, y, z)) { return; } -@@ -4571,6 +4878,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4587,6 +4894,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause @@ -29318,7 +29318,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers if (this.removalReason == null) { -@@ -4581,7 +4894,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4597,7 +4910,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.stopRiding(); } @@ -29327,7 +29327,7 @@ index be8213fa58e8305976c5ce16c9ff32130a26d42c..ace6c77be333e839b679b5cf3cd7c080 this.levelCallback.onRemove(removalReason); this.onRemoval(removalReason); // Paper start - Folia schedulers -@@ -4615,7 +4928,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4631,7 +4944,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean shouldBeSaved() { return (this.removalReason == null || this.removalReason.shouldSave()) && !this.isPassenger() diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index 428679de20..10e24def5c 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -126,6 +126,15 @@ private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0}; private long pistonDeltasGameTime; private EntityDimensions dimensions; +@@ -251,7 +_,7 @@ + private boolean onGroundNoBlocks = false; + private float crystalSoundIntensity; + private int lastCrystalSoundPlayTick; +- public boolean hasVisualFire; ++ public net.kyori.adventure.util.TriState visualFire = net.kyori.adventure.util.TriState.NOT_SET; // Paper - improve visual fire API + @Nullable + private BlockState inBlockState = null; + private final List> movementThisTick = new ObjectArrayList<>(); @@ -259,6 +_,41 @@ private final LongSet visitedBlocks = new LongOpenHashSet(); private final InsideBlockEffectApplier.StepBasedCollector insideEffectCollector = new InsideBlockEffectApplier.StepBasedCollector(); @@ -392,7 +401,12 @@ } this.checkBelowWorld(); -@@ -504,7 +_,12 @@ +@@ -500,11 +_,16 @@ + } + + public void setSharedFlagOnFire(boolean isOnFire) { +- this.setSharedFlag(0, isOnFire || this.hasVisualFire); ++ this.setSharedFlag(0, this.visualFire.toBooleanOrElse(isOnFire)); // Paper - improve visual fire API } public void checkBelowWorld() { @@ -761,6 +775,21 @@ Component customName = this.getCustomName(); if (customName != null) { RegistryOps registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE); +@@ -1848,9 +_,12 @@ + compound.putInt("TicksFrozen", this.getTicksFrozen()); + } + +- if (this.hasVisualFire) { +- compound.putBoolean("HasVisualFire", this.hasVisualFire); ++ // Paper start - improve visual fire API ++ if (this.visualFire.equals(net.kyori.adventure.util.TriState.TRUE)) { ++ compound.putBoolean("HasVisualFire", true); + } ++ compound.putString("Paper.FireOverride", visualFire.name()); ++ // Paper end + + if (!this.tags.isEmpty()) { + compound.store("Tags", TAG_LIST_CODEC, List.copyOf(this.tags)); @@ -1860,13 +_,13 @@ compound.store("data", CustomData.CODEC, this.customData); } @@ -820,6 +849,28 @@ Vec2 vec2 = compound.read("Rotation", Vec2.CODEC).orElse(Vec2.ZERO); this.setDeltaMovement(Math.abs(vec31.x) > 10.0 ? 0.0 : vec31.x, Math.abs(vec31.y) > 10.0 ? 0.0 : vec31.y, Math.abs(vec31.z) > 10.0 ? 0.0 : vec31.z); this.hasImpulse = true; +@@ -1921,7 +_,20 @@ + this.setNoGravity(compound.getBooleanOr("NoGravity", false)); + this.setGlowingTag(compound.getBooleanOr("Glowing", false)); + this.setTicksFrozen(compound.getIntOr("TicksFrozen", 0)); +- this.hasVisualFire = compound.getBooleanOr("HasVisualFire", false); ++ // Paper start - improve visual fire API ++ compound.getString("Paper.FireOverride").ifPresentOrElse( ++ override -> { ++ try { ++ this.visualFire = net.kyori.adventure.util.TriState.valueOf(override); ++ } catch (final Exception ignored) { ++ LOGGER.error("Unknown fire override {} for {}", override, this); ++ } ++ }, ++ () -> this.visualFire = compound.getBoolean("HasVisualFire") ++ .map(net.kyori.adventure.util.TriState::byBoolean) ++ .orElse(net.kyori.adventure.util.TriState.NOT_SET) ++ ); ++ // Paper end + this.customData = compound.read("data", CustomData.CODEC).orElse(CustomData.EMPTY); + this.tags.clear(); + compound.read("Tags", TAG_LIST_CODEC).ifPresent(this.tags::addAll); @@ -1932,6 +_,67 @@ } else { throw new IllegalStateException("Entity has invalid rotation"); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index cd145edc45..cfc926c97c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.UUID; import io.papermc.paper.entity.LookAnchor; import java.util.concurrent.CompletableFuture; +import net.kyori.adventure.util.TriState; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; @@ -395,13 +396,25 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { } @Override + @Deprecated public void setVisualFire(boolean fire) { - this.getHandle().hasVisualFire = fire; + setVisualFire(fire ? TriState.TRUE : TriState.NOT_SET); + } + + @Override + public void setVisualFire(final TriState fire) { + Preconditions.checkArgument(fire != null, "TriState cannot be null"); + this.getHandle().visualFire = fire; } @Override public boolean isVisualFire() { - return this.getHandle().hasVisualFire; + return getVisualFire().toBooleanOrElse(false); + } + + @Override + public TriState getVisualFire() { + return this.getHandle().visualFire; } @Override