Add back per player mob spawning

Also removes the `info.mobCategoryCounts.mergeInt` call that - at least from what I can tell - has been wrongly counting spawned mobs twice. The runner passed via `info::afterSpawn` already counts up that exact number in the same exact places (where `j`, the return value used here, is incremented)
This commit is contained in:
Nassim Jahnke 2024-10-27 11:56:51 +01:00
parent b6305644f9
commit 3cb16c9d91
No known key found for this signature in database
GPG Key ID: EF6771C01F6EF02F

View File

@ -5,10 +5,10 @@ Subject: [PATCH] Optional per player mob spawns
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 48b8fa3dea0244f9a0f4e0b8850b17a6d23b639c..56f7a63e65cce587512b77aafdb4ced18b43d024 100644 index bf43bdb43c5301c0e0954729bc531fb6a5045075..97a24cb410cf7d22a1a8edf8a5622d03a3d5a9c7 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java --- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -223,8 +223,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -231,8 +231,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} }
// Paper start // Paper start
@ -37,16 +37,25 @@ index 48b8fa3dea0244f9a0f4e0b8850b17a6d23b639c..56f7a63e65cce587512b77aafdb4ced1
// Paper end // Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 9ccb3ffc375298fda4dca97803e65e39df8493eb..e18c3e08f9fbfe17c797c1f96dce1b86fa41fab6 100644 index 4b5985c284faac7b06c0f99d53065f5060ecff4a..ba989879c43cb7d614198444fd2ead85ea71eae9 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -450,14 +450,26 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon @@ -503,7 +503,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
gameprofilerfiller.popPush("naturalSpawnCount"); gameprofilerfiller.popPush("shuffleChunks");
// Paper start - chunk tick iteration optimisation
this.shuffleRandom.setSeed(this.level.random.nextLong());
- Util.shuffle(list, this.shuffleRandom);
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
// Paper end - chunk tick iteration optimisation
this.tickChunks(gameprofilerfiller, j, list);
gameprofilerfiller.pop();
@@ -563,7 +563,19 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
profiler.popPush("naturalSpawnCount");
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
int k = this.distanceManager.getNaturalSpawnChunkCount(); int j = this.distanceManager.getNaturalSpawnChunkCount();
- NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(k, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap)); - NaturalSpawner.SpawnState spawnercreature_d = NaturalSpawner.createState(j, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
+ // Paper start - Optional per player mob spawns + // Paper start - Optional per player mob spawns
+ int naturalSpawnChunkCount = k; + final int naturalSpawnChunkCount = j;
+ NaturalSpawner.SpawnState spawnercreature_d; // moved down + NaturalSpawner.SpawnState spawnercreature_d; // moved down
+ if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled + if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
+ // re-set mob counts + // re-set mob counts
@ -61,19 +70,11 @@ index 9ccb3ffc375298fda4dca97803e65e39df8493eb..e18c3e08f9fbfe17c797c1f96dce1b86
this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings
this.lastSpawnState = spawnercreature_d; this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("spawnAndTick");
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
- Util.shuffle(list, this.level.random);
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.level.random); // Paper - per player mob spawns - do not need this when per-player is enabled
// Paper start - PlayerNaturallySpawnCreaturesEvent
int chunkRange = level.spigotConfig.mobSpawnRange;
chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 30f53916a9e49165bcfef2bea2c0b50a26f5a8a3..cb961d9051416626f499c1ca87107f1114433c94 100644 index 8c9148426f23cbbdfaf7ae66657d1a620f8bd853..8cc02ee9b1a710e35eb65a5a095681cc7dc542bb 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -276,6 +276,10 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple @@ -303,6 +303,10 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
public boolean queueHealthUpdatePacket; public boolean queueHealthUpdatePacket;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket; public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
// Paper end - cancellable death event // Paper end - cancellable death event
@ -85,10 +86,10 @@ index 30f53916a9e49165bcfef2bea2c0b50a26f5a8a3..cb961d9051416626f499c1ca87107f11
// CraftBukkit start // CraftBukkit start
public CraftPlayer.TransferCookieConnection transferCookieConnection; public CraftPlayer.TransferCookieConnection transferCookieConnection;
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f856fc113 100644 index 6eb69ebe688c1c52d5a5986dfc63cdd42e66687e..f3b52e61ddde25a60c1d178a7607b220ca01f770 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -67,6 +67,12 @@ public final class NaturalSpawner { @@ -71,6 +71,12 @@ public final class NaturalSpawner {
private NaturalSpawner() {} private NaturalSpawner() {}
public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) { public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) {
@ -101,7 +102,7 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator(); PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap(); Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
Iterator iterator = entities.iterator(); Iterator iterator = entities.iterator();
@@ -99,11 +105,16 @@ public final class NaturalSpawner { @@ -103,11 +109,16 @@ public final class NaturalSpawner {
spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge()); spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
} }
@ -119,17 +120,32 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
}); });
} }
} }
@@ -138,13 +149,35 @@ public final class NaturalSpawner { @@ -142,7 +153,7 @@ public final class NaturalSpawner {
continue; continue;
} }
- if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && info.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) { - if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) {
+ // Paper start - Optional per player mob spawns; only allow spawns upto the limit per chunk and update count afterwards + if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && (!worldserver.paperConfig().entities.spawning.perPlayerMobSpawns || spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit))) { // Paper - Optional per player mob spawns; remove global check, check later during the local one
+ int currEntityCount = info.mobCategoryCounts.getInt(enumcreaturetype); // CraftBukkit end
+ int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER; list.add(enumcreaturetype);
+ int difference = k1 - currEntityCount; }
+ @@ -161,12 +172,43 @@ public final class NaturalSpawner {
while (iterator.hasNext()) {
MobCategory enumcreaturetype = (MobCategory) iterator.next();
- if (info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos())) {
+ // Paper start - Optional per player mob spawns
+ final boolean canSpawn;
+ int maxSpawns = Integer.MAX_VALUE;
+ if (world.paperConfig().entities.spawning.perPlayerMobSpawns) { + if (world.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ // Copied from getFilteredSpawningCategories
+ int limit = enumcreaturetype.getMaxInstancesPerChunk();
+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
+ limit = world.getWorld().getSpawnLimit(spawnCategory);
+ }
+
+ // Apply per-player limit
+ int minDiff = Integer.MAX_VALUE; + int minDiff = Integer.MAX_VALUE;
+ final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange = + final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> inRange =
+ world.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); + world.moonrise$getNearbyPlayers().getPlayers(chunk.getPos(), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
@ -139,44 +155,44 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
+ minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear(backingSet[k], enumcreaturetype), minDiff); + minDiff = Math.min(limit - world.getChunkSource().chunkMap.getMobCountNear(backingSet[k], enumcreaturetype), minDiff);
+ } + }
+ } + }
+ difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; +
+ maxSpawns = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff;
+ canSpawn = maxSpawns > 0;
+ } else {
+ canSpawn = info.canSpawnForCategoryLocal(enumcreaturetype, chunk.getPos());
+ } + }
+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) { + if (canSpawn) {
+ // Paper end - Optional per player mob spawns + // Paper end - Optional per player mob spawns
// CraftBukkit end
Objects.requireNonNull(info); Objects.requireNonNull(info);
NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn; NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn;
Objects.requireNonNull(info); Objects.requireNonNull(info);
- NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn); - NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn);
+ // Paper start - Optional per player mob spawns + // Paper start - Optional per player mob spawns
+ int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, + NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn,
+ difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); + maxSpawns, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null);
+ info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum);
+ // Paper end - Optional per player mob spawns + // Paper end - Optional per player mob spawns
} }
} }
@@ -163,11 +196,17 @@ public final class NaturalSpawner { @@ -185,10 +227,15 @@ public final class NaturalSpawner {
// Paper end - Add mobcaps commands // Paper end - Add mobcaps commands
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
+ // Paper start - Optional per player mob spawns + // Paper start - Optional per player mob spawns
+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null); + spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
+ } + }
+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) { + public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - Optional per player mob spawns + // Paper end - Optional per player mob spawns
BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk); BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk);
if (blockposition.getY() >= world.getMinBuildHeight() + 1) { if (blockposition.getY() >= world.getMinY() + 1) {
- NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner); - NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner);
+ return NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns + NaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns, trackEntity); // Paper - Optional per player mob spawns
} }
+ return 0; // Paper - Optional per player mob spawns
} }
@VisibleForDebug @@ -200,7 +247,12 @@ public final class NaturalSpawner {
@@ -178,15 +217,21 @@ public final class NaturalSpawner {
}); });
} }
@ -184,39 +200,12 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null); + spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null);
+ } + }
+ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) { + public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer<Entity> trackEntity) {
+ // Paper end - Optional per player mob spawns + // Paper end - Optional per player mob spawns
StructureManager structuremanager = world.structureManager(); StructureManager structuremanager = world.structureManager();
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
int i = pos.getY(); int i = pos.getY();
BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn @@ -270,9 +322,14 @@ public final class NaturalSpawner {
+ int j = 0; // Paper - Optional per player mob spawns; moved up
if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
- int j = 0;
+ //int j = 0; // Paper - Optional per player mob spawns; moved up
int k = 0;
while (k < 3) {
@@ -228,14 +273,14 @@ public final class NaturalSpawner {
// Paper start - PreCreatureSpawnEvent
PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
if (doSpawning == PreSpawnStatus.ABORT) {
- return;
+ return j; // Paper - Optional per player mob spawns
}
if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
// Paper end - PreCreatureSpawnEvent
Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type);
if (entityinsentient == null) {
- return;
+ return j; // Paper - Optional per player mob spawns
}
entityinsentient.moveTo(d0, (double) i, d1, world.random.nextFloat() * 360.0F, 0.0F);
@@ -248,10 +293,15 @@ public final class NaturalSpawner {
++j; ++j;
++k1; ++k1;
runner.run(entityinsentient, chunk); runner.run(entityinsentient, chunk);
@ -228,21 +217,11 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
} }
// CraftBukkit end // CraftBukkit end
- if (j >= entityinsentient.getMaxSpawnClusterSize()) { - if (j >= entityinsentient.getMaxSpawnClusterSize()) {
- return;
+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns + if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper - Optional per player mob spawns
+ return j; // Paper - Optional per player mob spawns return;
} }
if (entityinsentient.isMaxGroupSizeReached(k1)) { @@ -545,7 +602,7 @@ public final class NaturalSpawner {
@@ -273,6 +323,7 @@ public final class NaturalSpawner {
}
}
+ return j; // Paper - Optional per player mob spawns
}
private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) {
@@ -523,7 +574,7 @@ public final class NaturalSpawner {
MobCategory enumcreaturetype = entitytypes.getCategory(); MobCategory enumcreaturetype = entitytypes.getCategory();
this.mobCategoryCounts.addTo(enumcreaturetype, 1); this.mobCategoryCounts.addTo(enumcreaturetype, 1);
@ -251,11 +230,3 @@ index ea6533c1ac218aa075da3401807a06fcb7892321..364510c0d0667e67aa3b25099a021f5f
} }
public int getSpawnableChunkCount() { public int getSpawnableChunkCount() {
@@ -539,6 +590,7 @@ public final class NaturalSpawner {
int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
// CraftBukkit end
+ if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper - Optional per player mob spawns
return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
}
}