From f7a5adaf57ec870ff908ecbb8b3adb2929b810ed Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Fri, 16 Jan 2026 17:00:39 +0800 Subject: [PATCH 1/5] Active weak-loaded entity management --- .../0291-Rewrite-entity-despawn-time.patch | 155 +++++++++++++++--- .../leaf/config/modules/opt/DespawnTime.java | 33 ++++ 2 files changed, 165 insertions(+), 23 deletions(-) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java diff --git a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch index 9e6b68047..30d2feeaa 100644 --- a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch +++ b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch @@ -19,11 +19,110 @@ 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/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65ad19d8175 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +@@ -51,6 +51,11 @@ public abstract class EntityLookup implements LevelEntityGetter { + protected final ConcurrentLong2ReferenceChainedHashTable entityById = new ConcurrentLong2ReferenceChainedHashTable<>(); + protected final ConcurrentHashMap entityByUUID = new ConcurrentHashMap<>(); + protected final EntityList accessibleEntities = new EntityList(); ++ // Leaf start - Rewrite entity despawn time ++ private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet weakEntities = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); ++ public final it.unimi.dsi.fastutil.objects.ReferenceArrayList weakEntityList = new it.unimi.dsi.fastutil.objects.ReferenceArrayList<>(); ++ private int weakCursor = 0; ++ // Leaf end - Rewrite entity despawn time + + public EntityLookup(final Level world, final LevelCallback worldCallback) { + this.world = world; +@@ -257,6 +262,21 @@ public abstract class EntityLookup implements LevelEntityGetter { + try { + final Boolean ticketBlockBefore = this.blockTicketUpdates(); + try { ++ // Leaf start - Rewrite entity despawn time ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) { ++ boolean isWeakNow = newVisibility == Visibility.TRACKED; ++ boolean wasWeakBefore = oldVisibility == Visibility.TRACKED; ++ ++ if (entity.despawnTime >= 0) { ++ if (isWeakNow && !wasWeakBefore) { ++ this.weakEntities.add(entity); ++ this.weakEntityList.add(entity); ++ } else if (!isWeakNow && wasWeakBefore) { ++ this.weakEntities.remove(entity); ++ } ++ } ++ } ++ // Leaf end - Rewrite entity despawn time + ((ChunkSystemEntity)entity).moonrise$setUpdatingSectionStatus(true); + try { + if (created) { +@@ -497,6 +517,7 @@ public abstract class EntityLookup implements LevelEntityGetter { + LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); + } + } ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.weakEntities.remove(entity); // Leaf - Rewrite entity despawn time + ((ChunkSystemEntity)entity).moonrise$setSectionX(Integer.MIN_VALUE); + ((ChunkSystemEntity)entity).moonrise$setSectionY(Integer.MIN_VALUE); + ((ChunkSystemEntity)entity).moonrise$setSectionZ(Integer.MIN_VALUE); +@@ -935,6 +956,44 @@ public abstract class EntityLookup implements LevelEntityGetter { + } + } + ++ // Leaf start - Rewrite entity despawn time ++ public void leaf$tickWeakEntities() { ++ if (this.weakEntityList.isEmpty()) return; ++ ++ int size = this.weakEntityList.size(); ++ int toProcess = Math.max(org.dreeam.leaf.config.modules.opt.DespawnTime.maxEntityToProcess, size >> 4); ++ ++ if (this.weakCursor >= size) this.weakCursor = 0; ++ ++ while (toProcess-- > 0 && !this.weakEntityList.isEmpty()) { ++ if (this.weakCursor >= this.weakEntityList.size()) { ++ this.weakCursor = 0; ++ } ++ ++ Entity entity = this.weakEntityList.get(this.weakCursor); ++ ++ if (entity == null || !this.weakEntities.contains(entity)) { ++ removeAtCursor(this.weakEntityList, this.weakCursor); ++ continue; ++ } ++ ++ if (entity.isExceedingDespawnTime(true)) { ++ entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); ++ removeAtCursor(this.weakEntityList, this.weakCursor); ++ } else { ++ this.weakCursor++; ++ } ++ } ++ } ++ ++ private static void removeAtCursor(List list, int index) { ++ int lastIndex = list.size() - 1; ++ if (index != lastIndex) { ++ list.set(index, list.get(lastIndex)); ++ } ++ list.remove(lastIndex); ++ } ++ // Leaf end - Rewrite entity despawn time + public static final class ChunkSlicesRegion { + + private final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE]; diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..e41087a52c5c0521728357431afd182080ab1291 100644 +index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..30d4a73138e12c73cd52517632bbd8b3c96d1f73 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1613,6 +1613,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1061,6 +1061,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.debugSynchronizers.tick(this.server.debugSubscribers()); + profilerFiller.pop(); + this.environmentAttributes().invalidateTickCache(); ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.moonrise$getEntityLookup().leaf$tickWeakEntities(); + if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { this.leaf$asyncTracker.onEntitiesTickEnd(); } // Leaf - Multithreaded tracker + } + +@@ -1613,6 +1614,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } else {entity.inactiveTick();} // Paper - EAR 2 profilerFiller.pop(); @@ -32,7 +131,7 @@ index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..e41087a52c5c0521728357431afd1820 for (Entity entity1 : entity.getPassengers()) { this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 } -@@ -1641,6 +1643,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1641,6 +1644,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - EAR 2 profilerFiller.pop(); @@ -42,12 +141,15 @@ index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..e41087a52c5c0521728357431afd1820 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 ece19e97fe6084b212cd0d7adc46b33768fe73f6..ff95d27720d8605cd58172ec695d254941632c2c 100644 +index ece19e97fe6084b212cd0d7adc46b33768fe73f6..75b3bedb71e948431a987e2ae508f3fb5329b2f0 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -377,6 +377,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -375,8 +375,9 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name + // Paper end + public boolean freezeLocked = false; // Paper - Freeze Tick Lock API public boolean fixedPose = false; // Paper - Expand Pose API - private final int despawnTime; // Paper - entity despawn time limit +- private final int despawnTime; // Paper - entity despawn time limit ++ public final int despawnTime; // Paper - entity despawn time limit // Leaf - Rewrite entity despawn time - private -> public public int totalEntityAge; // Paper - age-like counter for all entities + private int lastTickTime; // Leaf - Rewrite entity despawn time public boolean activatedPriorityReset = false; // Pufferfish - DAB @@ -69,7 +171,7 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..ff95d27720d8605cd58172ec695d2549 } public boolean isColliding(BlockPos pos, BlockState state) { -@@ -898,15 +901,50 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -898,15 +901,57 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name } public void tick() { @@ -90,26 +192,33 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..ff95d27720d8605cd58172ec695d2549 } + // 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; -+ } ++ private int calculateMissedTicks() { ++ return net.minecraft.server.MinecraftServer.currentTick - this.lastTickTime; ++ } ++ ++ public final boolean detectDespawnTime() { ++ boolean exceeded = this.isExceedingDespawnTime(false); ++ if (exceeded) { ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); + } -+ return false; ++ return exceeded; + } + -+ private int calculateMissedTicks() { -+ return net.minecraft.server.MinecraftServer.currentTick - this.lastTickTime; ++ public final boolean isExceedingDespawnTime(boolean isWeakLoaded) { ++ if (this.despawnTime >= 0) { ++ int compensatedAge = this.syncEntityAge(!isWeakLoaded); ++ return compensatedAge >= this.despawnTime; ++ } ++ return false; + } + -+ private void syncEntityAge() { ++ private int syncEntityAge(boolean writeBack) { + int missedTicks = this.calculateMissedTicks(); + if (missedTicks > 1) { ++ if (writeBack) return this.totalEntityAge + missedTicks; + this.totalEntityAge += missedTicks; + } ++ return this.totalEntityAge; + } + + public void updateLastTick() { @@ -120,15 +229,15 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..ff95d27720d8605cd58172ec695d2549 // 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 -@@ -2681,6 +2719,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -2681,6 +2726,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name if (this.maxAirTicks != this.getDefaultMaxAirSupply() && this.getDefaultMaxAirSupply() != this.level().purpurConfig.drowningAirTicks) { // Purpur - Drowning Settings output.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply()); } -+ this.syncEntityAge(); // Leaf - Rewrite entity despawn time ++ this.syncEntityAge(false); // Leaf - Rewrite entity despawn time output.putInt("Spigot.ticksLived", this.totalEntityAge); // Paper // CraftBukkit end output.storeNullable("CustomName", ComponentSerialization.CODEC, this.getCustomName()); -@@ -2840,7 +2879,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -2840,7 +2886,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name // CraftBukkit start // Spigot start @@ -137,11 +246,11 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..ff95d27720d8605cd58172ec695d2549 this.totalEntityAge = input.getIntOr("Spigot.ticksLived", 0); // Paper } // Spigot end -@@ -5431,9 +5470,10 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name +@@ -5431,9 +5477,10 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name @Override public boolean shouldBeSaved() { -+ this.syncEntityAge(); // Leaf - Rewrite entity despawn time ++ this.syncEntityAge(false); // 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 diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java new file mode 100644 index 000000000..b8c1292a8 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java @@ -0,0 +1,33 @@ +package org.dreeam.leaf.config.modules.opt; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; +import org.dreeam.leaf.config.annotations.Experimental; + +public class DespawnTime extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.PERF.getBaseKeyName() + ".despawn-time"; + } + + @Experimental + public static boolean activeWeakLoading = false; + public static int maxEntityToProcess = 20; + + @Override + public void onLoaded() { + activeWeakLoading = config.getBoolean(getBasePath() + ".active-weak-loading-despawn", activeWeakLoading, + config.pickStringRegionBased(""" + Active despawn check for weak-loaded entities. + This is an experimental feature.""", + """ + 启用主动弱加载实体消失检查, + 这是一个实验性功能.""")); + maxEntityToProcess = config.getInt(getBasePath() + ".max-entity-to-process", maxEntityToProcess, + config.pickStringRegionBased(""" + Maximum amount of entities to process per tick.""", + """ + 每刻处理最大实体数. + 最低为当前实体量的 5%.""")); + } +} From 750fdd1e57efdd5f201a210bd9f95fd1aaac19ae Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:20:08 +0800 Subject: [PATCH 2/5] invert condition --- .../features/0291-Rewrite-entity-despawn-time.patch | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch index 30d2feeaa..1295f7c28 100644 --- a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch +++ b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch @@ -141,7 +141,7 @@ index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..30d4a73138e12c73cd52517632bbd8b3 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 ece19e97fe6084b212cd0d7adc46b33768fe73f6..75b3bedb71e948431a987e2ae508f3fb5329b2f0 100644 +index ece19e97fe6084b212cd0d7adc46b33768fe73f6..4273c745f85e24b0a1936fd67e2109a4c2da4067 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -375,8 +375,9 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name @@ -215,8 +215,8 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..75b3bedb71e948431a987e2ae508f3fb + private int syncEntityAge(boolean writeBack) { + int missedTicks = this.calculateMissedTicks(); + if (missedTicks > 1) { -+ if (writeBack) return this.totalEntityAge + missedTicks; -+ this.totalEntityAge += missedTicks; ++ if (writeBack) return this.totalEntityAge += missedTicks; ++ return this.totalEntityAge + missedTicks; + } + return this.totalEntityAge; + } @@ -233,7 +233,7 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..75b3bedb71e948431a987e2ae508f3fb if (this.maxAirTicks != this.getDefaultMaxAirSupply() && this.getDefaultMaxAirSupply() != this.level().purpurConfig.drowningAirTicks) { // Purpur - Drowning Settings output.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply()); } -+ this.syncEntityAge(false); // Leaf - Rewrite entity despawn time ++ this.syncEntityAge(true); // Leaf - Rewrite entity despawn time output.putInt("Spigot.ticksLived", this.totalEntityAge); // Paper // CraftBukkit end output.storeNullable("CustomName", ComponentSerialization.CODEC, this.getCustomName()); @@ -250,7 +250,7 @@ index ece19e97fe6084b212cd0d7adc46b33768fe73f6..75b3bedb71e948431a987e2ae508f3fb @Override public boolean shouldBeSaved() { -+ this.syncEntityAge(false); // Leaf - Rewrite entity despawn time ++ this.syncEntityAge(true); // 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 From 9ff0f22d735b8ab7b9edcf8a652478bce490fd3e Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:04:21 -0500 Subject: [PATCH 3/5] [ci skip] Add missing comment --- .../features/0291-Rewrite-entity-despawn-time.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch index 1295f7c28..3081fed5e 100644 --- a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch +++ b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch @@ -111,14 +111,14 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65a private final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE]; diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..30d4a73138e12c73cd52517632bbd8b3c96d1f73 100644 +index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..0c13cf2a8c7dd90b311501dfb2c741e9b7dcc7b4 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1061,6 +1061,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.debugSynchronizers.tick(this.server.debugSubscribers()); profilerFiller.pop(); this.environmentAttributes().invalidateTickCache(); -+ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.moonrise$getEntityLookup().leaf$tickWeakEntities(); ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.moonrise$getEntityLookup().leaf$tickWeakEntities(); // Leaf - Rewrite entity despawn time if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { this.leaf$asyncTracker.onEntitiesTickEnd(); } // Leaf - Multithreaded tracker } From 0fe7dea8bd4d605888a2bb89a1e12101a79ce2c5 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:26:53 -0500 Subject: [PATCH 4/5] Improve config comments --- .../0291-Rewrite-entity-despawn-time.patch | 10 +++++----- .../leaf/config/modules/opt/DespawnTime.java | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch index 47a1c043d..7b905f3ca 100644 --- a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch +++ b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch @@ -20,7 +20,7 @@ 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/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65ad19d8175 100644 +index 703bf9c2a56b262e2719a1787584de537b8f12e0..3237dfae82913ec1ed4c0ee830db30771991164a 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java @@ -51,6 +51,11 @@ public abstract class EntityLookup implements LevelEntityGetter { @@ -40,7 +40,7 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65a final Boolean ticketBlockBefore = this.blockTicketUpdates(); try { + // Leaf start - Rewrite entity despawn time -+ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) { ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.proactiveWeakLoading) { + boolean isWeakNow = newVisibility == Visibility.TRACKED; + boolean wasWeakBefore = oldVisibility == Visibility.TRACKED; + @@ -61,7 +61,7 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65a LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); } } -+ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.weakEntities.remove(entity); // Leaf - Rewrite entity despawn time ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.proactiveWeakLoading) this.weakEntities.remove(entity); // Leaf - Rewrite entity despawn time ((ChunkSystemEntity)entity).moonrise$setSectionX(Integer.MIN_VALUE); ((ChunkSystemEntity)entity).moonrise$setSectionY(Integer.MIN_VALUE); ((ChunkSystemEntity)entity).moonrise$setSectionZ(Integer.MIN_VALUE); @@ -111,14 +111,14 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..90a7ea8c52c225f057b0e4dc8a8fe65a private final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE]; diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..0c13cf2a8c7dd90b311501dfb2c741e9b7dcc7b4 100644 +index 541c384187a0b20386ccc35200c7c4eb1ce62e9b..f359a411bbde9c561d2ca68d6f5bac2336238ff5 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -1061,6 +1061,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.debugSynchronizers.tick(this.server.debugSubscribers()); profilerFiller.pop(); this.environmentAttributes().invalidateTickCache(); -+ if (org.dreeam.leaf.config.modules.opt.DespawnTime.activeWeakLoading) this.moonrise$getEntityLookup().leaf$tickWeakEntities(); // Leaf - Rewrite entity despawn time ++ if (org.dreeam.leaf.config.modules.opt.DespawnTime.proactiveWeakLoading) this.moonrise$getEntityLookup().leaf$tickWeakEntities(); // Leaf - Rewrite entity despawn time if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { this.leaf$asyncTracker.onEntitiesTickEnd(); } // Leaf - Multithreaded tracker } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java index b8c1292a8..32b2a4b03 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java @@ -11,23 +11,23 @@ public String getBasePath() { } @Experimental - public static boolean activeWeakLoading = false; + public static boolean proactiveWeakLoading = false; public static int maxEntityToProcess = 20; @Override public void onLoaded() { - activeWeakLoading = config.getBoolean(getBasePath() + ".active-weak-loading-despawn", activeWeakLoading, + proactiveWeakLoading = config.getBoolean(getBasePath() + ".proactive-weak-loading-despawn", proactiveWeakLoading, config.pickStringRegionBased(""" - Active despawn check for weak-loaded entities. + Proactive despawn check for weak-loaded entities. This is an experimental feature.""", """ - 启用主动弱加载实体消失检查, - 这是一个实验性功能.""")); + 启用主动弱加载实体消失检查, + 这是一个实验性功能。""")); maxEntityToProcess = config.getInt(getBasePath() + ".max-entity-to-process", maxEntityToProcess, config.pickStringRegionBased(""" Maximum amount of entities to process per tick.""", """ - 每刻处理最大实体数. - 最低为当前实体量的 5%.""")); + 每刻处理最大实体数。 + 最低为当前实体量的 5%。""")); } } From b3f07ca87c55363662bae709b50b1bf6489e7167 Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:49:27 +0800 Subject: [PATCH 5/5] Remove entity process limit --- .../0291-Rewrite-entity-despawn-time.patch | 33 +++++++------------ .../leaf/config/modules/opt/DespawnTime.java | 7 ---- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch index 7b905f3ca..628ab465b 100644 --- a/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch +++ b/leaf-server/minecraft-patches/features/0291-Rewrite-entity-despawn-time.patch @@ -20,22 +20,21 @@ 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/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -index 703bf9c2a56b262e2719a1787584de537b8f12e0..3237dfae82913ec1ed4c0ee830db30771991164a 100644 +index 703bf9c2a56b262e2719a1787584de537b8f12e0..bdb12ee11716f622654a58e37b833d0a5cae94f0 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java -@@ -51,6 +51,11 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -51,6 +51,10 @@ public abstract class EntityLookup implements LevelEntityGetter { protected final ConcurrentLong2ReferenceChainedHashTable entityById = new ConcurrentLong2ReferenceChainedHashTable<>(); protected final ConcurrentHashMap entityByUUID = new ConcurrentHashMap<>(); protected final EntityList accessibleEntities = new EntityList(); + // Leaf start - Rewrite entity despawn time + private final it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet weakEntities = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); + public final it.unimi.dsi.fastutil.objects.ReferenceArrayList weakEntityList = new it.unimi.dsi.fastutil.objects.ReferenceArrayList<>(); -+ private int weakCursor = 0; + // Leaf end - Rewrite entity despawn time public EntityLookup(final Level world, final LevelCallback worldCallback) { this.world = world; -@@ -257,6 +262,21 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -257,6 +261,21 @@ public abstract class EntityLookup implements LevelEntityGetter { try { final Boolean ticketBlockBefore = this.blockTicketUpdates(); try { @@ -57,7 +56,7 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..3237dfae82913ec1ed4c0ee830db3077 ((ChunkSystemEntity)entity).moonrise$setUpdatingSectionStatus(true); try { if (created) { -@@ -497,6 +517,7 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -497,6 +516,7 @@ public abstract class EntityLookup implements LevelEntityGetter { LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")"); } } @@ -65,7 +64,7 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..3237dfae82913ec1ed4c0ee830db3077 ((ChunkSystemEntity)entity).moonrise$setSectionX(Integer.MIN_VALUE); ((ChunkSystemEntity)entity).moonrise$setSectionY(Integer.MIN_VALUE); ((ChunkSystemEntity)entity).moonrise$setSectionZ(Integer.MIN_VALUE); -@@ -935,6 +956,44 @@ public abstract class EntityLookup implements LevelEntityGetter { +@@ -935,6 +955,34 @@ public abstract class EntityLookup implements LevelEntityGetter { } } @@ -73,33 +72,23 @@ index 703bf9c2a56b262e2719a1787584de537b8f12e0..3237dfae82913ec1ed4c0ee830db3077 + public void leaf$tickWeakEntities() { + if (this.weakEntityList.isEmpty()) return; + -+ int size = this.weakEntityList.size(); -+ int toProcess = Math.max(org.dreeam.leaf.config.modules.opt.DespawnTime.maxEntityToProcess, size >> 4); -+ -+ if (this.weakCursor >= size) this.weakCursor = 0; -+ -+ while (toProcess-- > 0 && !this.weakEntityList.isEmpty()) { -+ if (this.weakCursor >= this.weakEntityList.size()) { -+ this.weakCursor = 0; -+ } -+ -+ Entity entity = this.weakEntityList.get(this.weakCursor); ++ for (int i = this.weakEntityList.size() - 1; i >= 0; i--) { ++ Entity entity = this.weakEntityList.get(i); + + if (entity == null || !this.weakEntities.contains(entity)) { -+ removeAtCursor(this.weakEntityList, this.weakCursor); ++ removeAtIndex(this.weakEntityList, i); + continue; + } + + if (entity.isExceedingDespawnTime(true)) { + entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); -+ removeAtCursor(this.weakEntityList, this.weakCursor); -+ } else { -+ this.weakCursor++; ++ this.weakEntities.remove(entity); ++ removeAtIndex(this.weakEntityList, i); + } + } + } + -+ private static void removeAtCursor(List list, int index) { ++ private static void removeAtIndex(List list, int index) { + int lastIndex = list.size() - 1; + if (index != lastIndex) { + list.set(index, list.get(lastIndex)); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java index 32b2a4b03..f37825cae 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/DespawnTime.java @@ -12,7 +12,6 @@ public String getBasePath() { @Experimental public static boolean proactiveWeakLoading = false; - public static int maxEntityToProcess = 20; @Override public void onLoaded() { @@ -23,11 +22,5 @@ public void onLoaded() { """ 启用主动弱加载实体消失检查, 这是一个实验性功能。""")); - maxEntityToProcess = config.getInt(getBasePath() + ".max-entity-to-process", maxEntityToProcess, - config.pickStringRegionBased(""" - Maximum amount of entities to process per tick.""", - """ - 每刻处理最大实体数。 - 最低为当前实体量的 5%。""")); } }