@@ -30,7 +30,7 @@ index 5c8a603e5800e1cc282d899e2a3d6b556295e13c..2dca1ceb886537ff96d0834844429e1c
3030 }
3131 // Paper end - Optional per player mob spawns
3232diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
33- index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..f5b58c181726536bedabd4b6f64769e312ef4257 100644
33+ index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..e9fd69a3f7a95635361c22f0ecdc558741d3a77f 100644
3434--- a/net/minecraft/server/level/ServerChunkCache.java
3535+++ b/net/minecraft/server/level/ServerChunkCache.java
3636@@ -70,11 +70,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
@@ -82,7 +82,7 @@ index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..f5b58c181726536bedabd4b6f64769e3
8282 // Paper start - per player mob spawning backoff
8383 for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
8484 player.mobCounts[ii] = 0;
85- @@ -525,34 +542,27 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
85+ @@ -525,34 +542,21 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
8686 player.mobBackoffCounts[ii] = newBackoff;
8787 }
8888 // Paper end - per player mob spawning backoff
@@ -92,7 +92,8 @@ index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..f5b58c181726536bedabd4b6f64769e3
9292 firstRunSpawnCounts = false;
9393 _pufferfish_spawnCountsReady.set(true);
9494 }
95- if (_pufferfish_spawnCountsReady.getAndSet(false)) {
95+ - if (_pufferfish_spawnCountsReady.getAndSet(false)) {
96+ + if (_pufferfish_spawnCountsReady.getAndSet(false) && level.paperConfig().entities.spawning.perPlayerMobSpawns) {
9697+ final int mapped = distanceManager.getNaturalSpawnChunkCount();
9798+ final Iterable<Entity> entities = this.level.getAllEntities();
9899 net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
@@ -117,30 +118,24 @@ index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..f5b58c181726536bedabd4b6f64769e3
117118- } finally {
118119- objectiterator.finishedIterating();
119120- }
120- + // Fix: Use proper mob cap calculator based on configuration
121- + LocalMobCapCalculator mobCapCalculator = !level.paperConfig().entities.spawning.perPlayerMobSpawns ?
122- + new LocalMobCapCalculator(chunkMap) : null;
123- +
124121+ // This ensures the caps are properly enforced by using the correct calculator
125- + lastSpawnState = NaturalSpawner.createState1 ( // Leaf - optimize mob spawning
122+ + lastSpawnState = NaturalSpawner.createStateAsync ( // Leaf - optimize mob spawning
126123+ mapped,
127124+ entities,
128- + this.level, // Leaf - optimize mob spawning
129- + mobCapCalculator, // This is the key fix - was previously null
130- + level.paperConfig().entities.spawning.perPlayerMobSpawns
125+ + this.level
131126+ );
132127 _pufferfish_spawnCountsReady.set(true);
133128 });
134129 }
135- @@ -611,6 +621 ,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
130+ @@ -611,6 +615 ,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
136131 chunkRange = Math.min(chunkRange, 8);
137132 entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
138133 entityPlayer.playerNaturallySpawnedEvent.callEvent();
139134+ this.level.natureSpawnChunkMap.addPlayer(entityPlayer); // Leaf - optimize mob spawning
140135 }
141136 // Paper end - PlayerNaturallySpawnCreaturesEvent
142137 boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
143- @@ -622,16 +633 ,34 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
138+ @@ -622,16 +627 ,34 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
144139 List<LevelChunk> list = this.spawningChunks;
145140
146141 try {
@@ -178,7 +173,7 @@ index 908cd08e33fed1c4cd4bd34c3e63cbbe84ffead4..f5b58c181726536bedabd4b6f64769e3
178173 list.clear();
179174 }
180175
181- @@ -649,7 +678 ,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
176+ @@ -649,7 +672 ,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
182177 }
183178
184179 if (!spawnCategories.isEmpty()) {
@@ -200,7 +195,7 @@ index 641875899b839d4c0012bd49a3f058258a06d99a..9b3863a7456d3d226a2d53eefdfc109b
200195 final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting
201196 ChunkPos pos = chunk.getPos();
202197diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
203- index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fead4f448b1 100644
198+ index bb655318f49242858e2c25d5469705c0c314ed85..b65616715fa29fdf33f2dfc73650c30f72160f48 100644
204199--- a/net/minecraft/world/level/NaturalSpawner.java
205200+++ b/net/minecraft/world/level/NaturalSpawner.java
206201@@ -68,6 +68,7 @@ public final class NaturalSpawner {
@@ -211,17 +206,13 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
211206 public static NaturalSpawner.SpawnState createState(
212207 int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
213208 ) {
214- @@ -108,9 +109,71 @@ public final class NaturalSpawner {
215- }
216- }
217-
218- - return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator);
219- + return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Leaf - optimize mob spawning
209+ @@ -111,6 +112,46 @@ public final class NaturalSpawner {
210+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator);
220211 }
221212
222213+ // Leaf start - optimize mob spawning
223- + public static NaturalSpawner.SpawnState createState1 (
224- + int spawnableChunkCount, Iterable<Entity> entities, ServerLevel level, LocalMobCapCalculator calculator, final boolean countMobs
214+ + public static NaturalSpawner.SpawnState createStateAsync (
215+ + int spawnableChunkCount, Iterable<Entity> entities, ServerLevel level
225216+ ) {
226217+ // Paper end - Optional per player mob spawns
227218+ PotentialCalculator potentialCalculator = new PotentialCalculator();
@@ -247,44 +238,22 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
247238+ potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
248239+ }
249240+
250- + if (calculator != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
251- + calculator.addMob(chunk.getPos(), category);
252- + }
253- +
254241+ map.addTo(category, 1);
255- + // Paper start - Optional per player mob spawns
256- + if (countMobs) {
257- + final int index = entity.getType().getCategory().ordinal();
258- + ++chunkCap.computeIfAbsent(chunk.getPos().longKey, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
259- + /*
260242+ final int index = entity.getType().getCategory().ordinal();
261- + final var inRange = level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
262- + if (inRange == null) {
263- + continue;
264- + }
265- +
266- + final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
267- + for (int i = 0, len = inRange.size(); i < len; i++) {
268- + final net.minecraft.server.level.ServerPlayer player = backingSet[i];
269- + if (player == null) continue;
270- + ++playerCap.computeIfAbsent(player, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
271- + }
272- + */
273- + }
274- + // Paper end - Optional per player mob spawns
243+ + ++chunkCap.computeIfAbsent(chunk.coordinateKey, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
275244+ }
276245+ }
277246+ }
278247+ }
279248+
280- + return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator , chunkCap);
249+ + return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, null , chunkCap);
281250+ }
282251+ // Leaf end - optimize mob spawning
283252+
284253 static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
285254 return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
286255 }
287- @@ -265,38 +328 ,78 @@ public final class NaturalSpawner {
256+ @@ -265,38 +306 ,78 @@ public final class NaturalSpawner {
288257 MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity, final boolean nothing
289258 // Paper PR end - throttle failed spawn attempts
290259 ) {
@@ -373,7 +342,7 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
373342 );
374343 if (randomSpawnMobAt.isEmpty()) {
375344 break;
376- @@ -307,7 +410 ,7 @@ public final class NaturalSpawner {
345+ @@ -307,7 +388 ,7 @@ public final class NaturalSpawner {
377346 }
378347
379348 // Paper start - PreCreatureSpawnEvent
@@ -382,7 +351,7 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
382351 // Paper start - per player mob count backoff
383352 if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
384353 level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category);
385- @@ -414,6 +517 ,44 @@ public final class NaturalSpawner {
354+ @@ -414,6 +495 ,44 @@ public final class NaturalSpawner {
386355 && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
387356 return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent
388357 }
@@ -427,7 +396,7 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
427396
428397 @Nullable
429398 private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) {
430- @@ -449,6 +590 ,17 @@ public final class NaturalSpawner {
399+ @@ -449,6 +568 ,17 @@ public final class NaturalSpawner {
431400 : mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
432401 }
433402
@@ -445,7 +414,7 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
445414 private static boolean canSpawnMobAt(
446415 ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos
447416 ) {
448- @@ -463,8 +615 ,22 @@ public final class NaturalSpawner {
417+ @@ -463,8 +593 ,22 @@ public final class NaturalSpawner {
449418 : generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(null, pos) : level.getBiome(pos)), structureManager, cetagory, pos); // Leaf - cache getBiome
450419 }
451420
@@ -469,7 +438,7 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
469438 Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS);
470439 return structure != null && structureManager.getStructureAt(pos, structure).isValid();
471440 } else {
472- @@ -472,6 +638 ,19 @@ public final class NaturalSpawner {
441+ @@ -472,6 +616 ,19 @@ public final class NaturalSpawner {
473442 }
474443 }
475444
@@ -489,30 +458,42 @@ index bb655318f49242858e2c25d5469705c0c314ed85..97a2c3f56dcf1c85cbab6cb3ce2a1fea
489458 private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
490459 ChunkPos pos = chunk.getPos();
491460 int i = pos.getMinBlockX() + level.random.nextInt(16);
492- @@ -612,18 +791,21 @@ public final class NaturalSpawner {
461+ @@ -612,6 +769,7 @@ public final class NaturalSpawner {
493462 @Nullable
494463 private EntityType<?> lastCheckedType;
495464 private double lastCharge;
496465+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> chunkCap; // Leaf - optimize mob spawning
497466
498467 SpawnState(
499468 int spawnableChunkCount,
500- Object2IntOpenHashMap<MobCategory> mobCategoryCounts,
501- PotentialCalculator spawnPotential,
502- - LocalMobCapCalculator localMobCapCalculator
503- + LocalMobCapCalculator localMobCapCalculator,
504- + it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap // Leaf - optimize mob spawning
505- ) {
506- this.spawnableChunkCount = spawnableChunkCount;
507- this.mobCategoryCounts = mobCategoryCounts;
469+ @@ -624,8 +782,26 @@ public final class NaturalSpawner {
508470 this.spawnPotential = spawnPotential;
509471 this.localMobCapCalculator = localMobCapCalculator;
510472 this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
511- + this.chunkCap = playerCap ; // Leaf - optimize mob spawning
473+ + this.chunkCap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>() ; // Leaf - optimize mob spawning
512474 }
513475
476+ + // Leaf start - optimize mob spawning
477+ + SpawnState(
478+ + int spawnableChunkCount,
479+ + Object2IntOpenHashMap<MobCategory> mobCategoryCounts,
480+ + PotentialCalculator spawnPotential,
481+ + LocalMobCapCalculator localMobCapCalculator,
482+ + it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap
483+ + ) {
484+ + this.spawnableChunkCount = spawnableChunkCount;
485+ + this.mobCategoryCounts = mobCategoryCounts;
486+ + this.spawnPotential = spawnPotential;
487+ + this.localMobCapCalculator = localMobCapCalculator;
488+ + this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
489+ + this.chunkCap = playerCap;
490+ + }
491+ + // Leaf end - optimize mob spawning
492+ +
514493 private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) {
515- @@ -680,5 +862,32 @@ public final class NaturalSpawner {
494+ this.lastCheckedPos = pos;
495+ this.lastCheckedType = entityType;
496+ @@ -680,5 +856,32 @@ public final class NaturalSpawner {
516497 boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
517498 return this.localMobCapCalculator.canSpawn(category, chunkPos);
518499 }
0 commit comments