From 68ea7d459586f55e2a2ff366cc1dc7db3b936aea Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 19 Mar 2025 13:48:23 -0400 Subject: [PATCH 1/3] Validate recipe display index before retrieving it --- ...Remove-streams-on-InsideBrownianWalk.patch | 1 - ...e-display-index-before-retrieving-it.patch | 23 +++++++++++++++++++ todos.md | 3 --- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0146-Validate-recipe-display-index-before-retrieving-it.patch diff --git a/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch index eb8838f0..e056ca71 100644 --- a/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch +++ b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch @@ -6,7 +6,6 @@ Subject: [PATCH] Remove streams on InsideBrownianWalk This method is maninly visible when ton of villagers suddenly wants to sleep Safe optimization, no need to provide any numbers. - diff --git a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java b/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java index cbde74f4b6d586a5f80cdd675573441636bf682d..3ac3059ce23d6291aa4e96ed5441159133d3e965 100644 --- a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java diff --git a/leaf-server/minecraft-patches/features/0146-Validate-recipe-display-index-before-retrieving-it.patch b/leaf-server/minecraft-patches/features/0146-Validate-recipe-display-index-before-retrieving-it.patch new file mode 100644 index 00000000..97bc0eee --- /dev/null +++ b/leaf-server/minecraft-patches/features/0146-Validate-recipe-display-index-before-retrieving-it.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Wed, 19 Mar 2025 13:32:39 -0400 +Subject: [PATCH] Validate recipe display index before retrieving it + + +diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java +index 4bd1b514f91c0a2c9261b41211a4a341f784a995..5b69c4927174611d62f0f4698215ab97c827c7f5 100644 +--- a/net/minecraft/world/item/crafting/RecipeManager.java ++++ b/net/minecraft/world/item/crafting/RecipeManager.java +@@ -206,7 +206,11 @@ public class RecipeManager extends SimplePreparableReloadListener imp + + @Nullable + public RecipeManager.ServerDisplayInfo getRecipeFromDisplay(RecipeDisplayId display) { +- return this.allDisplays.get(display.index()); ++ // Leaf start - Validate recipe display index before retrieving it ++ final int index = display.index(); ++ ++ return this.allDisplays.size() > index ? this.allDisplays.get(index) : null; ++ // Leaf end - Validate recipe display index before retrieving it + } + + public void listDisplaysForRecipe(ResourceKey> recipe, Consumer output) { diff --git a/todos.md b/todos.md index d96538ab..22e3b6eb 100644 --- a/todos.md +++ b/todos.md @@ -6,11 +6,8 @@ # Leaf TODOs - [ ] refactor leaves protocol manager opt and pr it. -- [ ] Check and apply work patches - [ ] Transfer patch notes to file for Gale and Leaf - [ ] Add spigot config unknown message to leaf docs - [ ] Add server full join config explanation to docs - [ ] Add purpur config changes to docs moved config -- [ ] Merge changes from Tramontane - [ ] check Dont send useless entity packets -- [ ] Check lithium fastutil and equipment tracking missing stuff? From e34228f143536a9e519a06a895b8c7d641b21367 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 19 Mar 2025 13:52:03 -0400 Subject: [PATCH 2/3] Readd `Paper PR: Throttle failed spawn attempts` --- ...er-PR-Throttle-failed-spawn-attempts.patch | 140 -------- ...er-PR-Throttle-failed-spawn-attempts.patch | 320 ++++++++++++++++++ ...er-PR-Throttle-failed-spawn-attempts.patch | 54 +++ 3 files changed, 374 insertions(+), 140 deletions(-) delete mode 100644 leaf-archived-patches/work/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch create mode 100644 leaf-server/minecraft-patches/features/0147-Paper-PR-Throttle-failed-spawn-attempts.patch create mode 100644 leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/leaf-archived-patches/work/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-archived-patches/work/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch deleted file mode 100644 index 7d647f24..00000000 --- a/leaf-archived-patches/work/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: froobynooby -Date: Wed, 17 Jul 2024 18:46:11 +0930 -Subject: [PATCH] Paper PR: Throttle failed spawn attempts - -Original license: GPLv3 -Original project: https://github.com/PaperMC/Paper -Paper pull request: https://github.com/PaperMC/Paper/pull/11099 - -For example config in paper-world-defaults.yml -``` - spawning-throttle: - failed-attempts-threshold: 1200 - throttled-ticks-per-spawn: - ambient: 10 # default value in bukkit.yml tickers-per * 10 - axolotls: 10 - creature: 4000 - monster: 10 - underground_water_creature: 10 - water_ambient: 10 - water_creature: 10 -``` - -This patch adds the option to use longer ticks-per-spawn for a given -mob type in chunks where spawn attempts are consecutively failing. - -This behaviour is particularly useful on servers where players build -mob farms. Mob farm designs often require making surrounding chunks -spawnproof, which causes the server to waste CPU cycles trying to spawn mobs in -vain. Throttling spawn attempts in suspected spawnproof chunks improves -performance without noticeably advantaging or disadvantaging the mob farm. - -diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -index c867796f625813797f167610ad443c4be5a7561e..04d13abd8d98f4e72732afbdbb9719d835389793 100644 ---- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -@@ -190,6 +190,15 @@ public class WorldConfiguration extends ConfigurationPart { - @MergeMap - public Reference2IntMap ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); - -+ public SpawningThrottle spawningThrottle; -+ -+ public class SpawningThrottle extends ConfigurationPart { -+ public IntOr.Disabled failedAttemptsThreshold = IntOr.Disabled.DISABLED; -+ -+ @MergeMap -+ public Reference2IntMap throttledTicksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); -+ } -+ - @ConfigSerializable - public record DespawnRangePair(@Required DespawnRange hard, @Required DespawnRange soft) { - public static DespawnRangePair createDefault() { -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 95a000dbf1a05cfd8182f15c0e0bbf7023578974..455c7e66fc1ff28c4f720a01ff11aa408446b42e 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -138,8 +138,14 @@ public final class NaturalSpawner { - boolean spawnThisTick = true; - int limit = enumcreaturetype.getMaxInstancesPerChunk(); - SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype); -+ // Paper start - throttle failed spawn attempts -+ long ticksPerSpawn = world.ticksPerSpawnCategory.getLong(spawnCategory); -+ if (world.paperConfig().entities.spawning.spawningThrottle.failedAttemptsThreshold.test(threshold -> chunk.failedSpawnAttempts[enumcreaturetype.ordinal()] > threshold)) { -+ ticksPerSpawn = Math.max(ticksPerSpawn, world.paperConfig().entities.spawning.spawningThrottle.throttledTicksPerSpawn.getOrDefault(enumcreaturetype, -1)); -+ } -+ // Paper end - throttle failed spawn attempts - if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { -- spawnThisTick = world.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % world.ticksPerSpawnCategory.getLong(spawnCategory) == 0; -+ spawnThisTick = world.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts - limit = world.getWorld().getSpawnLimit(spawnCategory); - } - -@@ -172,8 +178,13 @@ public final class NaturalSpawner { - - Objects.requireNonNull(info); - // Paper start - Optional per player mob spawns -- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, -- difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); -+ // Paper start - throttle failed spawn attempts -+ if (NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null) == 0) { -+ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()]++; -+ } else { -+ chunk.failedSpawnAttempts[enumcreaturetype.ordinal()] = 0; -+ } -+ // Paper end - throttle failed spawn attempts - // Paper end - Optional per player mob spawns - } - } -diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index 5bb8fe022f580a626a99324f53515890a99b798d..5cee82d3d17739858e47f24ec5340233f09d0713 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -84,6 +84,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom - public final Map blockEntities = new Object2ObjectOpenHashMap(); - protected final LevelHeightAccessor levelHeightAccessor; - protected final LevelChunkSection[] sections; -+ public final long[] failedSpawnAttempts = new long[net.minecraft.world.entity.MobCategory.values().length]; // Paper - throttle failed spawn attempts - - // Leaf start - Matter - Feature Secure Seed - private boolean slimeChunk; -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -index 4bd048387651250135f963303c78c17f8473cfee..8257177404b23e131143017f3a7d835ad447f9fb 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -@@ -302,6 +302,16 @@ public class ChunkSerializer { - ((ChunkAccess) object1).addPackedPostProcess(nbttaglist2.getShort(i1), j1); - } - } -+ // Paper start - throttle failed spawn attempts -+ if (nbt.contains("Paper.FailedSpawnAttempts", Tag.TAG_COMPOUND)) { -+ CompoundTag failedSpawnAttemptsTag = nbt.getCompound("Paper.FailedSpawnAttempts"); -+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) { -+ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName(), Tag.TAG_LONG)) { -+ ((ChunkAccess) object1).failedSpawnAttempts[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLong(mobCategory.getSerializedName()); -+ } -+ } -+ } -+ // Paper end - throttle failed spawn attempts - - ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.loadLightHook(world, chunkPos, nbt, (ChunkAccess)object1); // Paper - rewrite chunk system - note: it's ok to pass the raw value instead of wrapped - -@@ -545,6 +555,18 @@ public class ChunkSerializer { - nbttagcompound.put("ChunkBukkitValues", chunk.persistentDataContainer.toTagCompound()); - } - // CraftBukkit end -+ // Paper start - throttle failed spawn attempts -+ CompoundTag failedSpawnAttemptsTag = new CompoundTag(); -+ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) { -+ long failedSpawnAttempts = chunk.failedSpawnAttempts[mobCategory.ordinal()]; -+ if (failedSpawnAttempts > 0) { -+ failedSpawnAttemptsTag.putLong(mobCategory.getSerializedName(), failedSpawnAttempts); -+ } -+ } -+ if (!failedSpawnAttemptsTag.isEmpty()) { -+ nbttagcompound.put("Paper.FailedSpawnAttempts", failedSpawnAttemptsTag); -+ } -+ // Paper end - throttle failed spawn attempts - ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.saveLightHook(world, chunk, nbttagcompound); // Paper - rewrite chunk system - return nbttagcompound; - } diff --git a/leaf-server/minecraft-patches/features/0147-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/minecraft-patches/features/0147-Paper-PR-Throttle-failed-spawn-attempts.patch new file mode 100644 index 00000000..94e2d7b8 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0147-Paper-PR-Throttle-failed-spawn-attempts.patch @@ -0,0 +1,320 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: froobynooby +Date: Wed, 17 Jul 2024 18:46:11 +0930 +Subject: [PATCH] Paper PR: Throttle failed spawn attempts + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper +Paper pull request: https://github.com/PaperMC/Paper/pull/11099 + +For example config in paper-world-defaults.yml +``` + spawning-throttle: + failed-attempts-threshold: 1200 + throttled-ticks-per-spawn: + ambient: 10 # default value in bukkit.yml tickers-per * 10 + axolotls: 10 + creature: 4000 + monster: 10 + underground_water_creature: 10 + water_ambient: 10 + water_creature: 10 +``` + +This patch adds the option to use longer ticks-per-spawn for a given +mob type in chunks where spawn attempts are consecutively failing. + +This behaviour is particularly useful on servers where players build +mob farms. Mob farm designs often require making surrounding chunks +spawnproof, which causes the server to waste CPU cycles trying to spawn mobs in +vain. Throttling spawn attempts in suspected spawnproof chunks improves +performance without noticeably advantaging or disadvantaging the mob farm. + +diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java +index d3f5242fc66529bf3137da4d505a6cf55e749e43..ce2621a87dec1befb016b3437ceb2d02ed6d0b75 100644 +--- a/net/minecraft/world/level/NaturalSpawner.java ++++ b/net/minecraft/world/level/NaturalSpawner.java +@@ -164,10 +164,21 @@ public final class NaturalSpawner { + // Copied from getFilteredSpawningCategories + int limit = mobCategory.getMaxInstancesPerChunk(); + SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(mobCategory); ++ // Paper start - throttle failed spawn attempts ++ boolean spawnThisTick = true; ++ long ticksPerSpawn = level.ticksPerSpawnCategory.getLong(spawnCategory); ++ long ticksPerSpawnTmp = ticksPerSpawn; ++ io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.SpawningThrottle spawningThrottle = level.paperConfig().entities.spawning.spawningThrottle; ++ if (spawningThrottle.failedAttemptsThreshold.test(threshold -> chunk.failedSpawnAttempts[mobCategory.ordinal()] > threshold)) { ++ ticksPerSpawn = Math.max(ticksPerSpawn, spawningThrottle.throttledTicksPerSpawn.getOrDefault(mobCategory, -1)); ++ } ++ // Paper end - throttle failed spawn attempts + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { ++ spawnThisTick = ticksPerSpawnTmp != 0 && level.getGameTime() % ticksPerSpawn == 0; // Paper - throttle failed spawn attempts + limit = level.getWorld().getSpawnLimit(spawnCategory); + } + ++ if (!spawningThrottle.failedAttemptsThreshold.enabled() || spawnThisTick) { // Paper - throttle failed spawn attempts + // Apply per-player limit + int minDiff = Integer.MAX_VALUE; + final ca.spottedleaf.moonrise.common.list.ReferenceList inRange = +@@ -181,12 +192,20 @@ public final class NaturalSpawner { + + maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; + canSpawn = maxSpawns > 0; ++ } else { canSpawn = false; } // Paper - throttle failed spawn attempts + } else { + canSpawn = spawnState.canSpawnForCategoryLocal(mobCategory, chunk.getPos()); + } + if (canSpawn) { +- spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn, +- maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); ++ // Paper start - throttle failed spawn attempts ++ int spawnCount = spawnCategoryForChunk(mobCategory, level, chunk, spawnState::canSpawn, spawnState::afterSpawn, ++ maxSpawns, level.paperConfig().entities.spawning.perPlayerMobSpawns ? level.getChunkSource().chunkMap::updatePlayerMobTypeMap : null, false); ++ if (spawnCount == 0) { ++ chunk.failedSpawnAttempts[mobCategory.ordinal()]++; ++ } else { ++ chunk.failedSpawnAttempts[mobCategory.ordinal()] = 0; ++ } ++ // Paper end - throttle failed spawn attempts + // Paper end - Optional per player mob spawns + } + } +@@ -210,12 +229,21 @@ public final class NaturalSpawner { + } + public static void spawnCategoryForChunk( + MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer trackEntity ++ // Paper start - throttle failed spawn attempts ++ ) { ++ spawnCategoryForChunk(category, level, chunk, filter, callback, maxSpawns, trackEntity, false); ++ } ++ public static int spawnCategoryForChunk( ++ MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final Consumer trackEntity, final boolean nothing ++ // Paper end - throttle failed spawn attempts + ) { + // Paper end - Optional per player mob spawns + BlockPos randomPosWithin = getRandomPosWithin(level, chunk); + if (randomPosWithin.getY() >= level.getMinY() + 1) { +- spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity); // Paper - Optional per player mob spawns ++ return spawnCategoryForPosition(category, level, chunk, randomPosWithin, filter, callback, maxSpawns, trackEntity, false); // Paper - Optional per player mob spawns // Paper - throttle failed spawn attempts + } ++ ++ return 0; // Paper - throttle failed spawn attempts + } + + @VisibleForDebug +@@ -235,15 +263,23 @@ public final class NaturalSpawner { + } + public static void spawnCategoryForPosition( + MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity ++ // Paper start - throttle failed spawn attempts ++ ) { ++ spawnCategoryForPosition(category, level, chunk, pos, filter, callback, maxSpawns, trackEntity, false); ++ } ++ public static int spawnCategoryForPosition( ++ MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer trackEntity, final boolean nothing ++ // Paper end - throttle failed spawn attempts + ) { + // Paper end - Optional per player mob spawns + StructureManager structureManager = level.structureManager(); + ChunkGenerator generator = level.getChunkSource().getGenerator(); + int y = pos.getY(); ++ int i = 0; // Paper - throttle failed spawn attempts + BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn + if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); +- int i = 0; ++ //int i = 0; // Paper - throttle failed spawn attempts - move up + + for (int i1 = 0; i1 < 3; i1++) { + int x = pos.getX(); +@@ -284,13 +320,13 @@ public final class NaturalSpawner { + } + // Paper end - per player mob count backoff + if (doSpawning == PreSpawnStatus.ABORT) { +- return; ++ return i; // Paper - throttle failed spawn attempts + } + if (doSpawning == PreSpawnStatus.SUCCESS && filter.test(spawnerData.type, mutableBlockPos, chunk)) { + // Paper end - PreCreatureSpawnEvent + Mob mobForSpawn = getMobForSpawn(level, spawnerData.type); + if (mobForSpawn == null) { +- return; ++ return i; // Paper - throttle failed spawn attempts + } + + mobForSpawn.moveTo(d, y, d1, level.random.nextFloat() * 360.0F, 0.0F); +@@ -313,7 +349,7 @@ public final class NaturalSpawner { + } + // CraftBukkit end + if (i >= mobForSpawn.getMaxSpawnClusterSize() || i >= maxSpawns) { // Paper - Optional per player mob spawns +- return; ++ return i; // Paper - throttle failed spawn attempts + } + + if (mobForSpawn.isMaxGroupSizeReached(i3)) { +@@ -326,6 +362,8 @@ public final class NaturalSpawner { + } + } + } ++ ++ return i; // Paper - throttle failed spawn attempts + } + + private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) { +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index 3a6db5bc0c8be7d68e15317a621c1965fdc3a9bd..50a9903367f49ece2a267d10944b1515c7b93859 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -91,6 +91,7 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + private boolean slimeChunk; + private boolean hasComputedSlimeChunk; + // Leaf end - Matter - Secure Seed ++ public final long[] failedSpawnAttempts = new long[net.minecraft.world.entity.MobCategory.values().length]; // Paper - throttle failed spawn attempts + + // Paper start - rewrite chunk system + private volatile ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles; +diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +index c0939c311c554a4660b80725294663bab7915733..e2df93b2500a74c4cecac1515f3991967a07a052 100644 +--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java ++++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +@@ -92,6 +92,7 @@ public record SerializableChunkData( + List blockEntities, + CompoundTag structureData + , @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer ++ , @Nullable long[] failedSpawnAttempts // Paper - throttle failed spawn attempts + ) { + public static final Codec> BLOCK_STATE_CODEC = PalettedContainer.codecRW( + Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray +@@ -216,6 +217,19 @@ public record SerializableChunkData( + lists[i] = list4; + } + ++ // Paper start - throttle failed spawn attempts ++ long[] failedSpawnAttemptsData = null; ++ if (tag.contains("Paper.FailedSpawnAttempts", net.minecraft.nbt.Tag.TAG_COMPOUND)) { ++ failedSpawnAttemptsData = new long[net.minecraft.world.entity.MobCategory.values().length]; ++ CompoundTag failedSpawnAttemptsTag = tag.getCompound("Paper.FailedSpawnAttempts"); ++ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.level.NaturalSpawner.SPAWNING_CATEGORIES) { ++ if (failedSpawnAttemptsTag.contains(mobCategory.getSerializedName(), net.minecraft.nbt.Tag.TAG_LONG)) { ++ failedSpawnAttemptsData[mobCategory.ordinal()] = failedSpawnAttemptsTag.getLong(mobCategory.getSerializedName()); ++ } ++ } ++ } ++ // Paper end - throttle failed spawn attempts ++ + List list5 = Lists.transform(tag.getList("entities", 10), tag1 -> (CompoundTag)tag1); + List list6 = Lists.transform(tag.getList("block_entities", 10), tag1 -> (CompoundTag)tag1); + CompoundTag compound1 = tag.getCompound("structures"); +@@ -294,6 +308,7 @@ public record SerializableChunkData( + list6, + compound1 + , tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues ++ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts + ); + } + } +@@ -450,6 +465,15 @@ public record SerializableChunkData( + chunkAccess.addPackedPostProcess(this.postProcessingSections[i], i); + } + ++ // Paper start - throttle failed spawn attempts ++ long[] failedSpawnAttemptsData = this.failedSpawnAttempts; ++ if (failedSpawnAttemptsData != null) { ++ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) { ++ System.arraycopy(failedSpawnAttemptsData, 0, chunkAccess.failedSpawnAttempts, 0, failedSpawnAttemptsData.length); ++ } ++ } ++ // Paper end - throttle failed spawn attempts ++ + if (chunkType == ChunkType.LEVELCHUNK) { + return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight + } else { +@@ -603,6 +627,7 @@ public record SerializableChunkData( + persistentDataContainer = chunk.persistentDataContainer.toTagCompound(); + } + // CraftBukkit end ++ final long[] failedSpawnAttemptsData = chunk.failedSpawnAttempts; // Paper - throttle failed spawn attempts + return new SerializableChunkData( + level.registryAccess().lookupOrThrow(Registries.BIOME), + pos, +@@ -623,6 +648,7 @@ public record SerializableChunkData( + list1, + compoundTag + , persistentDataContainer // CraftBukkit - persistentDataContainer ++ , failedSpawnAttemptsData // Paper - throttle failed spawn attempts + ); + } + } +@@ -719,6 +745,21 @@ public record SerializableChunkData( + compoundTag.put("ChunkBukkitValues", this.persistentDataContainer); + } + // CraftBukkit end ++ // Paper start - throttle failed spawn attempts ++ CompoundTag failedSpawnAttemptsTag = new CompoundTag(); ++ long[] failedSpawnAttemptsData = this.failedSpawnAttempts; ++ if (failedSpawnAttemptsData != null) { ++ for (net.minecraft.world.entity.MobCategory mobCategory : net.minecraft.world.entity.MobCategory.values()) { ++ long failedAttempts = failedSpawnAttemptsData[mobCategory.ordinal()]; ++ if (failedAttempts > 0) { ++ failedSpawnAttemptsTag.putLong(mobCategory.getSerializedName(), failedAttempts); ++ } ++ } ++ } ++ if (!failedSpawnAttemptsTag.isEmpty()) { ++ compoundTag.put("Paper.FailedSpawnAttempts", failedSpawnAttemptsTag); ++ } ++ // Paper end - throttle failed spawn attempts + // Paper start - starlight + if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) { + // clobber vanilla value to force vanilla to relight +@@ -947,4 +988,50 @@ public record SerializableChunkData( + } + // Paper end - starlight - convert from record + } ++ ++ // Paper start - throttle failed spawn attempts ++ // For plugin compatibility ++ public SerializableChunkData( ++ Registry biomeRegistry, ++ ChunkPos chunkPos, ++ int minSectionY, ++ long lastUpdateTime, ++ long inhabitedTime, ++ ChunkStatus chunkStatus, ++ @Nullable BlendingData.Packed blendingData, ++ @Nullable BelowZeroRetrogen belowZeroRetrogen, ++ UpgradeData upgradeData, ++ @Nullable long[] carvingMask, ++ Map heightmaps, ++ ChunkAccess.PackedTicks packedTicks, ++ ShortList[] postProcessingSections, ++ boolean lightCorrect, ++ List sectionData, ++ List entities, ++ List blockEntities, ++ CompoundTag structureData, ++ @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer ++ ) { ++ this(biomeRegistry, ++ chunkPos, ++ minSectionY, ++ lastUpdateTime, ++ inhabitedTime, ++ chunkStatus, ++ blendingData, ++ belowZeroRetrogen, ++ upgradeData, ++ carvingMask, ++ heightmaps, ++ packedTicks, ++ postProcessingSections, ++ lightCorrect, ++ sectionData, ++ entities, ++ blockEntities, ++ structureData, ++ persistentDataContainer, ++ null); ++ } ++ // Paper end - throttle failed spawn attempts + } diff --git a/leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch b/leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch new file mode 100644 index 00000000..94c9aad5 --- /dev/null +++ b/leaf-server/paper-patches/features/0032-Paper-PR-Throttle-failed-spawn-attempts.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: froobynooby +Date: Wed, 17 Jul 2024 18:46:11 +0930 +Subject: [PATCH] Paper PR: Throttle failed spawn attempts + +Original license: GPLv3 +Original project: https://github.com/PaperMC/Paper +Paper pull request: https://github.com/PaperMC/Paper/pull/11099 + +For example config in paper-world-defaults.yml +``` + spawning-throttle: + failed-attempts-threshold: 1200 + throttled-ticks-per-spawn: + ambient: 10 # default value in bukkit.yml tickers-per * 10 + axolotls: 10 + creature: 4000 + monster: 10 + underground_water_creature: 10 + water_ambient: 10 + water_creature: 10 +``` + +This patch adds the option to use longer ticks-per-spawn for a given +mob type in chunks where spawn attempts are consecutively failing. + +This behaviour is particularly useful on servers where players build +mob farms. Mob farm designs often require making surrounding chunks +spawnproof, which causes the server to waste CPU cycles trying to spawn mobs in +vain. Throttling spawn attempts in suspected spawnproof chunks improves +performance without noticeably advantaging or disadvantaging the mob farm. + +diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java +index 7bfa7aa30c1181587c7632f920f48348d2493ea4..d838c90f98c6593404c77d0aab8655c0d15905c4 100644 +--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java +@@ -181,6 +181,17 @@ public class WorldConfiguration extends ConfigurationPart { + @MergeMap + public Reference2IntMap ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); + ++ // Paper start - throttle failed spawn attempts ++ public SpawningThrottle spawningThrottle; ++ ++ public class SpawningThrottle extends ConfigurationPart { ++ public IntOr.Disabled failedAttemptsThreshold = IntOr.Disabled.DISABLED; ++ ++ @MergeMap ++ public Reference2IntMap throttledTicksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); ++ } ++ // Paper end - throttle failed spawn attempts ++ + @ConfigSerializable + public record DespawnRangePair(@Required DespawnRange hard, @Required DespawnRange soft) { + public static DespawnRangePair createDefault() { From d122a89765112a8cfda92d10abf8a12b5476473b Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:07:40 -0400 Subject: [PATCH 3/3] [ci skip] cleanup --- ...ntities-in-NearestLivingEntitySensor.patch | 26 ++++------- ...AndInteract-and-NearestVisibleLiving.patch | 44 +++++++++---------- ...Remove-streams-on-InsideBrownianWalk.patch | 16 +++---- 3 files changed, 35 insertions(+), 51 deletions(-) diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 0fedddc9..79f5e72e 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,18 +12,10 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc66b5bcc0 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..9ef531551bb135d4b6a1ca9c3320e35fa62d7c41 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -1,7 +1,6 @@ - package net.minecraft.world.entity.ai.sensing; - - import com.google.common.collect.ImmutableSet; --import java.util.Comparator; - import java.util.List; - import java.util.Set; - import net.minecraft.server.level.ServerLevel; -@@ -13,18 +12,92 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; +@@ -13,18 +13,92 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; import net.minecraft.world.phys.AABB; public class NearestLivingEntitySensor extends Sensor { @@ -63,7 +55,7 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc + bits[i] = Double.doubleToRawLongBits(reference.distanceToSqr(entities[i])); + } + -+ fastRadixSort(entities, bits, 0, entities.length-1, 62); ++ fastRadixSort(entities, bits, 0, entities.length - 1, 62); + return entities; + } + @@ -87,19 +79,19 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..5ba8bc485d1c06f16060789dd42a82cc + } + } + -+ if (low < j) fastRadixSort(ents, bits, low, j, bit-1); -+ if (i < high) fastRadixSort(ents, bits, i, high, bit-1); ++ if (low < j) fastRadixSort(ents, bits, low, j, bit - 1); ++ if (i < high) fastRadixSort(ents, bits, i, high, bit - 1); + } + + private void insertionSort(LivingEntity[] ents, long[] bits, int low, int high) { -+ for (int i = low+1; i <= high; i++) { ++ for (int i = low + 1; i <= high; i++) { + int j = i; + LivingEntity e = ents[j]; + long b = bits[j]; + -+ while (j > low && bits[j-1] > b) { -+ ents[j] = ents[j-1]; -+ bits[j] = bits[j-1]; ++ while (j > low && bits[j - 1] > b) { ++ ents[j] = ents[j - 1]; ++ bits[j] = bits[j - 1]; + j--; + } + diff --git a/leaf-server/minecraft-patches/features/0144-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch b/leaf-server/minecraft-patches/features/0144-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch index f860a13e..74e7a213 100644 --- a/leaf-server/minecraft-patches/features/0144-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch +++ b/leaf-server/minecraft-patches/features/0144-Optimize-SetLookAndInteract-and-NearestVisibleLiving.patch @@ -9,7 +9,7 @@ Paper: 3859ms Leaf : 1363ms (-64% reduction) diff --git a/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java b/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java -index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4b59613d6 100644 +index 13359e484486a5280f408955fe2a365cd3c34a43..48c30c8b686fdc82481bd72d6d10f541e9eb0217 100644 --- a/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java +++ b/net/minecraft/world/entity/ai/behavior/SetLookAndInteract.java @@ -9,7 +9,7 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; @@ -17,16 +17,17 @@ index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4 public class SetLookAndInteract { public static BehaviorControl create(EntityType entityType, int maxDist) { - int i = maxDist * maxDist; -+ final int maxDistSq = maxDist * maxDist; ++ final int maxDistSq = maxDist * maxDist; // Leaf - Optimize SetLookAndInteract and NearestVisibleLivingEntities return BehaviorBuilder.create( instance -> instance.group( instance.registered(MemoryModuleType.LOOK_TARGET), -@@ -19,16 +19,20 @@ public class SetLookAndInteract { +@@ -19,16 +19,22 @@ public class SetLookAndInteract { .apply( instance, (lookTarget, interactionTarget, nearestVisibleLivingEntities) -> (level, entity, gameTime) -> { - Optional optional = instance.get(nearestVisibleLivingEntities) - .findClosest(nearEntity -> nearEntity.distanceToSqr(entity) <= i && entityType.equals(nearEntity.getType())); ++ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities + // Check entity type first as it's likely cheaper than distance calculation + NearestVisibleLivingEntities entities = instance.get(nearestVisibleLivingEntities); + Optional optional = entities.findClosest( @@ -46,33 +47,22 @@ index 13359e484486a5280f408955fe2a365cd3c34a43..d326bf267123ff26d85e63aeb273baf4 + interactionTarget.set(livingEntity); + lookTarget.set(new EntityTracker(livingEntity, true)); + return true; ++ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities } ) ); diff --git a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java -index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90aaa6e6af 100644 +index 2b973a3ba7d65330fa4690e71e5321c28457ec61..82de8e3bc462638bffbb43456076de2670d76540 100644 --- a/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java +++ b/net/minecraft/world/entity/ai/memory/NearestVisibleLivingEntities.java -@@ -2,10 +2,11 @@ package net.minecraft.world.entity.ai.memory; - - import com.google.common.collect.Iterables; - import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; -+import it.unimi.dsi.fastutil.objects.ObjectArrayList; -+import it.unimi.dsi.fastutil.objects.ObjectList; - import java.util.List; - import java.util.Optional; - import java.util.function.Predicate; --import java.util.stream.Stream; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.entity.LivingEntity; - import net.minecraft.world.entity.ai.sensing.Sensor; -@@ -32,9 +33,26 @@ public class NearestVisibleLivingEntities { +@@ -32,11 +32,30 @@ public class NearestVisibleLivingEntities { } public Optional findClosest(Predicate predicate) { - for (LivingEntity livingEntity : this.nearbyEntities) { - if (predicate.test(livingEntity) && this.lineOfSightTest.test(livingEntity)) { - return Optional.of(livingEntity); ++ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities + // Early return if no entities + if (this.nearbyEntities.isEmpty()) { + return Optional.empty(); @@ -95,15 +85,21 @@ index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90 + } } } ++ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities -@@ -45,8 +63,14 @@ public class NearestVisibleLivingEntities { - return Iterables.filter(this.nearbyEntities, target -> predicate.test(target) && this.lineOfSightTest.test(target)); // Leaf - Optimize baby villager sensor - diff on change + return Optional.empty(); + } +@@ -46,8 +65,20 @@ public class NearestVisibleLivingEntities { } -- public Stream find(Predicate predicate) { + public Stream find(Predicate predicate) { - return this.nearbyEntities.stream().filter(target -> predicate.test(target) && this.lineOfSightTest.test(target)); -+ public List find(Predicate predicate) { -+ ObjectList result = new ObjectArrayList<>(); ++ return this.nearbyEntities.stream().filter(target -> predicate.test(target) && this.lineOfSightTest.test(target)); // Leaf - Optimize SetLookAndInteract and NearestVisibleLivingEntities - diff on change ++ } ++ ++ // Leaf start - Optimize SetLookAndInteract and NearestVisibleLivingEntities ++ public List findWithList(Predicate predicate) { ++ it.unimi.dsi.fastutil.objects.ObjectList result = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); + for (LivingEntity entity : this.nearbyEntities) { + if (predicate.test(entity) && this.lineOfSightTest.test(entity)) { + result.add(entity); @@ -111,5 +107,7 @@ index 2b973a3ba7d65330fa4690e71e5321c28457ec61..99a3c44b95ed0a14d272c5eb07b7db90 + } + return result; } ++ // Leaf end - Optimize SetLookAndInteract and NearestVisibleLivingEntities public boolean contains(LivingEntity entity) { + return this.nearbyEntities.contains(entity) && this.lineOfSightTest.test(entity); diff --git a/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch index e056ca71..f8c87c74 100644 --- a/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch +++ b/leaf-server/minecraft-patches/features/0145-Remove-streams-on-InsideBrownianWalk.patch @@ -7,27 +7,20 @@ This method is maninly visible when ton of villagers suddenly wants to sleep Safe optimization, no need to provide any numbers. diff --git a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java b/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java -index cbde74f4b6d586a5f80cdd675573441636bf682d..3ac3059ce23d6291aa4e96ed5441159133d3e965 100644 +index cbde74f4b6d586a5f80cdd675573441636bf682d..2324b941c7513692608b5146bc90c8e6124d2ee6 100644 --- a/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java +++ b/net/minecraft/world/entity/ai/behavior/InsideBrownianWalk.java -@@ -2,6 +2,7 @@ package net.minecraft.world.entity.ai.behavior; - - import java.util.Collections; - import java.util.List; -+import java.util.ArrayList; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.world.entity.PathfinderMob; -@@ -20,16 +21,29 @@ public class InsideBrownianWalk { +@@ -20,16 +20,31 @@ public class InsideBrownianWalk { return false; } else { BlockPos blockPos = mob.blockPosition(); - List list = BlockPos.betweenClosedStream(blockPos.offset(-1, -1, -1), blockPos.offset(1, 1, 1)) - .map(BlockPos::immutable) - .collect(Util.toMutableList()); ++ // Leaf start - Remove streams on InsideBrownianWalk + BlockPos minPos = blockPos.offset(-1, -1, -1); + BlockPos maxPos = blockPos.offset(1, 1, 1); -+ List list = new ArrayList<>(); ++ List list = new java.util.ArrayList<>(); + + for (int x = minPos.getX(); x <= maxPos.getX(); x++) { + for (int y = minPos.getY(); y <= maxPos.getY(); y++) { @@ -53,6 +46,7 @@ index cbde74f4b6d586a5f80cdd675573441636bf682d..3ac3059ce23d6291aa4e96ed54411591 + break; + } + } ++ // Leaf end - Remove streams on InsideBrownianWalk + return true; }