From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Tue, 9 Nov 2077 00:00:00 +0800 Subject: [PATCH] Rewrite entity despawn time HOW IT WORKS: In the default implementation, totalEntityAge only increments when an entity is actively ticked. Consequently, if an entity resides in a weak-loaded chunk, its aging process effectively pauses, allowing it to exist far beyond the configured despawn time. This patch addresses this discrepancy by synchronizing entity age with absolute server time: Introduces lastTickTime to record the MinecraftServer.currentTick when an entity was last ticked. Once the entity is ticked again or unloaded, the system calculates the elapsed time (currentTick - lastTickTime), and adds it to the totalEntityAge. Also, it moves the despawn logic of projectiles to their own tick method (see ThrowableProjectile#tick), making it faster to despawn massive stuck projectiles. Benchmarks (60,000 snowballs): Before: Around 120 seconds, server thread is struggling to process projectile physics. After: 1.5 seconds Brings the ability to despawn weak-loaded entities once they are ticked, a solution for https://github.com/PaperMC/Paper/issues/12986 diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index cb1be4947ea2d85c6dcdc7424a4b712dcce0a40e..01f7390f3fb2a423cec373a2bb5ccbee57278783 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1509,6 +1509,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe entity.tick(); entity.postTick(); // CraftBukkit } else {entity.inactiveTick();} // Paper - EAR 2 + entity.updateLastTick(); // Leaf - Rewrite entity despawn time for (Entity entity1 : entity.getPassengers()) { this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 @@ -1533,6 +1534,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe ridingEntity.positionRider(passengerEntity); } // Paper end - EAR 2 + passengerEntity.updateLastTick(); // Leaf - Rewrite entity despawn time for (Entity entity : passengerEntity.getPassengers()) { this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2 diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index fef583625899091b13439e0d459b7538c866d712..e84ff9d5a1b495170b503dece72f7766b347c2d0 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -373,6 +373,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean fixedPose = false; // Paper - Expand Pose API private final int despawnTime; // Paper - entity despawn time limit public int totalEntityAge; // Paper - age-like counter for all entities + private int lastTickTime; // Leaf - Rewrite entity despawn time public boolean activatedPriorityReset = false; // Pufferfish - DAB public int activatedPriority = org.dreeam.leaf.config.modules.opt.DynamicActivationofBrain.maximumActivationPrio; // Pufferfish - DAB (golf score) public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges @@ -402,6 +403,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private int sectionZ = Integer.MIN_VALUE; private boolean updatingSectionStatus; private static final boolean enableFMA = Boolean.getBoolean("Leaf.enableFMA"); // Leaf - Optimize Entity distanceTo + private final boolean shouldSkipBaseDespawnCheck = this instanceof net.minecraft.world.entity.projectile.ThrowableProjectile; // Leaf - Rewrite entity despawn time @Override public final boolean moonrise$isHardColliding() { @@ -626,6 +628,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setPos(0.0, 0.0, 0.0); this.eyeHeight = this.dimensions.eyeHeight(); this.despawnTime = level == null || type == EntityType.PLAYER ? -1 : level.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit + this.updateLastTick(); // Leaf - Rewrite entity despawn time } public boolean isColliding(BlockPos pos, BlockState state) { @@ -887,15 +890,50 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public void tick() { + // Leaf start - Rewrite entity despawn time + if (!this.shouldSkipBaseDespawnCheck && this.detectDespawnTime()) { + return; + } + /* // Paper start - entity despawn time limit if (this.despawnTime >= 0 && this.totalEntityAge >= this.despawnTime) { this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); return; } // Paper end - entity despawn time limit + */ + // Leaf end - Rewrite entity despawn time this.baseTick(); } + // Leaf start - Rewrite entity despawn time + protected final boolean detectDespawnTime() { + this.syncEntityAge(); + if (this.despawnTime >= 0) { + if (this.totalEntityAge >= this.despawnTime) { + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); + return true; + } + } + return false; + } + + private int calculateMissedTicks() { + return net.minecraft.server.MinecraftServer.currentTick - this.lastTickTime; + } + + private void syncEntityAge() { + int missedTicks = this.calculateMissedTicks(); + if (missedTicks > 1) { + this.totalEntityAge += missedTicks; + } + } + + public void updateLastTick() { + this.lastTickTime = net.minecraft.server.MinecraftServer.currentTick; + } + // Leaf end - Rewrite entity despawn time + // CraftBukkit start public void postTick() { // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle @@ -2564,6 +2602,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess if (this.maxAirTicks != this.getDefaultMaxAirSupply()) { output.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply()); } + this.syncEntityAge(); // Leaf - Rewrite entity despawn time output.putInt("Spigot.ticksLived", this.totalEntityAge); // Paper // CraftBukkit end output.storeNullable("CustomName", ComponentSerialization.CODEC, this.getCustomName()); @@ -2721,7 +2760,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // CraftBukkit start // Spigot start - if (this instanceof net.minecraft.world.entity.LivingEntity) { + if (true || this instanceof net.minecraft.world.entity.LivingEntity) { // Leaf - Rewrite entity despawn time this.totalEntityAge = input.getIntOr("Spigot.ticksLived", 0); // Paper } // Spigot end @@ -5320,9 +5359,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public boolean shouldBeSaved() { + this.syncEntityAge(); // Leaf - Rewrite entity despawn time return (this.removalReason == null || this.removalReason.shouldSave()) && !this.isPassenger() - && (!this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()); // Paper - rewrite chunk system + && (!this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()) && (this.despawnTime < 0 || this.totalEntityAge < this.despawnTime); // Paper - rewrite chunk system // Leaf - Rewrite entity despawn time } @Override diff --git a/net/minecraft/world/entity/projectile/ThrowableProjectile.java b/net/minecraft/world/entity/projectile/ThrowableProjectile.java index 4c61f4552dc64b1347c7b8dfe9502aac11c75888..be0ebc017bfe92fb2916a9e40971ad19614a33b4 100644 --- a/net/minecraft/world/entity/projectile/ThrowableProjectile.java +++ b/net/minecraft/world/entity/projectile/ThrowableProjectile.java @@ -44,6 +44,11 @@ public abstract class ThrowableProjectile extends Projectile { @Override public void tick() { + // Leaf start - Rewrite entity despawn time - Check earlier for projectiles + if (this.detectDespawnTime()) { + return; + } + // Leaf end - Rewrite entity despawn time - Check earlier for projectiles this.handleFirstTickBubbleColumn(); this.applyGravity(); this.applyInertia();