aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch')
-rw-r--r--patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch676
1 files changed, 676 insertions, 0 deletions
diff --git a/patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch b/patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch
new file mode 100644
index 0000000000..ef00c13ded
--- /dev/null
+++ b/patch-remap/mache-spigotflower/net/minecraft/world/level/NaturalSpawner.java.patch
@@ -0,0 +1,676 @@
+--- a/net/minecraft/world/level/NaturalSpawner.java
++++ b/net/minecraft/world/level/NaturalSpawner.java
+@@ -27,16 +27,16 @@
+ import net.minecraft.util.random.WeightedRandomList;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.Mob;
+ import net.minecraft.world.entity.MobCategory;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.SpawnPlacements;
+ import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.level.biome.Biome;
+ import net.minecraft.world.level.biome.MobSpawnSettings;
+ import net.minecraft.world.level.block.Blocks;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.chunk.ChunkAccess;
+ import net.minecraft.world.level.chunk.ChunkGenerator;
+ import net.minecraft.world.level.chunk.LevelChunk;
+@@ -45,9 +45,14 @@
+ import net.minecraft.world.level.levelgen.structure.Structure;
+ import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure;
+ import net.minecraft.world.level.material.FluidState;
+-import net.minecraft.world.level.pathfinder.PathComputationType;
++import net.minecraft.world.level.pathfinder.PathMode;
++import net.minecraft.world.level.storage.LevelData;
+ import net.minecraft.world.phys.Vec3;
+ import org.slf4j.Logger;
++import org.bukkit.craftbukkit.util.CraftSpawnCategory;
++import org.bukkit.entity.SpawnCategory;
++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
++// CraftBukkit end
+
+ public final class NaturalSpawner {
+
+@@ -56,112 +61,127 @@
+ public static final int SPAWN_DISTANCE_CHUNK = 8;
+ public static final int SPAWN_DISTANCE_BLOCK = 128;
+ static final int MAGIC_NUMBER = (int) Math.pow(17.0D, 2.0D);
+- private static final MobCategory[] SPAWNING_CATEGORIES = (MobCategory[]) Stream.of(MobCategory.values()).filter((mobcategory) -> {
+- return mobcategory != MobCategory.MISC;
++ private static final MobCategory[] SPAWNING_CATEGORIES = (MobCategory[]) Stream.of(MobCategory.values()).filter((enumcreaturetype) -> {
++ return enumcreaturetype != MobCategory.MISC;
+ }).toArray((i) -> {
+ return new MobCategory[i];
+ });
+
+ private NaturalSpawner() {}
+
+- public static NaturalSpawner.SpawnState createState(int i, Iterable<Entity> iterable, NaturalSpawner.ChunkGetter naturalspawner_chunkgetter, LocalMobCapCalculator localmobcapcalculator) {
+- PotentialCalculator potentialcalculator = new PotentialCalculator();
++ public static NaturalSpawner.SpawnState createState(int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator) {
++ PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator();
+ Object2IntOpenHashMap<MobCategory> object2intopenhashmap = new Object2IntOpenHashMap();
+- Iterator iterator = iterable.iterator();
++ Iterator iterator = entities.iterator();
+
+ while (iterator.hasNext()) {
+ Entity entity = (Entity) iterator.next();
+
+ if (entity instanceof Mob) {
+- Mob mob = (Mob) entity;
++ Mob entityinsentient = (Mob) entity;
+
+- if (mob.isPersistenceRequired() || mob.requiresCustomPersistence()) {
++ if (entityinsentient.isPersistenceRequired() || entityinsentient.requiresCustomPersistence()) {
+ continue;
+ }
+ }
+
+- MobCategory mobcategory = entity.getType().getCategory();
++ MobCategory enumcreaturetype = entity.getType().getCategory();
+
+- if (mobcategory != MobCategory.MISC) {
+- BlockPos blockpos = entity.blockPosition();
++ if (enumcreaturetype != MobCategory.MISC) {
++ BlockPos blockposition = entity.blockPosition();
+
+- naturalspawner_chunkgetter.query(ChunkPos.asLong(blockpos), (levelchunk) -> {
+- MobSpawnSettings.MobSpawnCost mobspawnsettings_mobspawncost = getRoughBiome(blockpos, levelchunk).getMobSettings().getMobSpawnCost(entity.getType());
++ chunkGetter.query(ChunkPos.asLong(blockposition), (chunk) -> {
++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entity.getType());
+
+- if (mobspawnsettings_mobspawncost != null) {
+- potentialcalculator.addCharge(entity.blockPosition(), mobspawnsettings_mobspawncost.charge());
++ if (biomesettingsmobs_b != null) {
++ spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge());
+ }
+
+ if (entity instanceof Mob) {
+- localmobcapcalculator.addMob(levelchunk.getPos(), mobcategory);
++ calculator.addMob(chunk.getPos(), enumcreaturetype);
+ }
+
+- object2intopenhashmap.addTo(mobcategory, 1);
++ object2intopenhashmap.addTo(enumcreaturetype, 1);
+ });
+ }
+ }
+
+- return new NaturalSpawner.SpawnState(i, object2intopenhashmap, potentialcalculator, localmobcapcalculator);
++ return new NaturalSpawner.SpawnState(spawnableChunkCount, object2intopenhashmap, spawnercreatureprobabilities, calculator);
+ }
+
+- static Biome getRoughBiome(BlockPos blockpos, ChunkAccess chunkaccess) {
+- return (Biome) chunkaccess.getNoiseBiome(QuartPos.fromBlock(blockpos.getX()), QuartPos.fromBlock(blockpos.getY()), QuartPos.fromBlock(blockpos.getZ())).value();
++ static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
++ return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
+ }
+
+- public static void spawnForChunk(ServerLevel serverlevel, LevelChunk levelchunk, NaturalSpawner.SpawnState naturalspawner_spawnstate, boolean flag, boolean flag1, boolean flag2) {
+- serverlevel.getProfiler().push("spawner");
+- MobCategory[] amobcategory = NaturalSpawner.SPAWNING_CATEGORIES;
+- int i = amobcategory.length;
++ public static void spawnForChunk(ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnState spawnState, boolean spawnFriendlies, boolean spawnMonsters, boolean forcedDespawn) {
++ level.getProfiler().push("spawner");
++ MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES;
++ int i = aenumcreaturetype.length;
+
++ LevelData worlddata = level.getLevelData(); // CraftBukkit - Other mob type spawn tick rate
++
+ for (int j = 0; j < i; ++j) {
+- MobCategory mobcategory = amobcategory[j];
++ MobCategory enumcreaturetype = aenumcreaturetype[j];
++ // CraftBukkit start - Use per-world spawn limits
++ boolean spawnThisTick = true;
++ int limit = enumcreaturetype.getMaxInstancesPerChunk();
++ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype);
++ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
++ spawnThisTick = level.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(spawnCategory) == 0;
++ limit = level.getWorld().getSpawnLimit(spawnCategory);
++ }
+
+- if ((flag || !mobcategory.isFriendly()) && (flag1 || mobcategory.isFriendly()) && (flag2 || !mobcategory.isPersistent()) && naturalspawner_spawnstate.canSpawnForCategory(mobcategory, levelchunk.getPos())) {
+- Objects.requireNonNull(naturalspawner_spawnstate);
+- NaturalSpawner.SpawnPredicate naturalspawner_spawnpredicate = naturalspawner_spawnstate::canSpawn;
++ if (!spawnThisTick || limit == 0) {
++ continue;
++ }
+
+- Objects.requireNonNull(naturalspawner_spawnstate);
+- spawnCategoryForChunk(mobcategory, serverlevel, levelchunk, naturalspawner_spawnpredicate, naturalspawner_spawnstate::afterSpawn);
++ if ((spawnFriendlies || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (forcedDespawn || !enumcreaturetype.isPersistent()) && spawnState.canSpawnForCategory(enumcreaturetype, chunk.getPos(), limit)) {
++ // CraftBukkit end
++ Objects.requireNonNull(spawnState);
++ NaturalSpawner.SpawnPredicate spawnercreature_c = spawnState::canSpawn;
++
++ Objects.requireNonNull(spawnState);
++ spawnCategoryForChunk(enumcreaturetype, level, chunk, spawnercreature_c, spawnState::afterSpawn);
+ }
+ }
+
+- serverlevel.getProfiler().pop();
++ level.getProfiler().pop();
+ }
+
+- public static void spawnCategoryForChunk(MobCategory mobcategory, ServerLevel serverlevel, LevelChunk levelchunk, NaturalSpawner.SpawnPredicate naturalspawner_spawnpredicate, NaturalSpawner.AfterSpawnCallback naturalspawner_afterspawncallback) {
+- BlockPos blockpos = getRandomPosWithin(serverlevel, levelchunk);
++ public static void spawnCategoryForChunk(MobCategory category, ServerLevel level, LevelChunk chunk, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback) {
++ BlockPos blockposition = getRandomPosWithin(level, chunk);
+
+- if (blockpos.getY() >= serverlevel.getMinBuildHeight() + 1) {
+- spawnCategoryForPosition(mobcategory, serverlevel, levelchunk, blockpos, naturalspawner_spawnpredicate, naturalspawner_afterspawncallback);
++ if (blockposition.getY() >= level.getMinBuildHeight() + 1) {
++ spawnCategoryForPosition(category, level, chunk, blockposition, filter, callback);
+ }
+ }
+
+ @VisibleForDebug
+- public static void spawnCategoryForPosition(MobCategory mobcategory, ServerLevel serverlevel, BlockPos blockpos) {
+- spawnCategoryForPosition(mobcategory, serverlevel, serverlevel.getChunk(blockpos), blockpos, (entitytype, blockpos1, chunkaccess) -> {
++ public static void spawnCategoryForPosition(MobCategory category, ServerLevel level, BlockPos pos) {
++ spawnCategoryForPosition(category, level, level.getChunk(pos), pos, (entitytypes, blockposition1, ichunkaccess) -> {
+ return true;
+- }, (mob, chunkaccess) -> {
++ }, (entityinsentient, ichunkaccess) -> {
+ });
+ }
+
+- public static void spawnCategoryForPosition(MobCategory mobcategory, ServerLevel serverlevel, ChunkAccess chunkaccess, BlockPos blockpos, NaturalSpawner.SpawnPredicate naturalspawner_spawnpredicate, NaturalSpawner.AfterSpawnCallback naturalspawner_afterspawncallback) {
+- StructureManager structuremanager = serverlevel.structureManager();
+- ChunkGenerator chunkgenerator = serverlevel.getChunkSource().getGenerator();
+- int i = blockpos.getY();
+- BlockState blockstate = chunkaccess.getBlockState(blockpos);
++ public static void spawnCategoryForPosition(MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback) {
++ StructureManager structuremanager = level.structureManager();
++ ChunkGenerator chunkgenerator = level.getChunkSource().getGenerator();
++ int i = pos.getY();
++ IBlockData iblockdata = chunk.getBlockState(pos);
+
+- if (!blockstate.isRedstoneConductor(chunkaccess, blockpos)) {
+- BlockPos.MutableBlockPos blockpos_mutableblockpos = new BlockPos.MutableBlockPos();
++ if (!iblockdata.isRedstoneConductor(chunk, pos)) {
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+ int j = 0;
+ int k = 0;
+
+ while (k < 3) {
+- int l = blockpos.getX();
+- int i1 = blockpos.getZ();
++ int l = pos.getX();
++ int i1 = pos.getZ();
+ boolean flag = true;
+- MobSpawnSettings.SpawnerData mobspawnsettings_spawnerdata = null;
+- SpawnGroupData spawngroupdata = null;
+- int j1 = Mth.ceil(serverlevel.random.nextFloat() * 4.0F);
++ MobSpawnSettings.SpawnerData biomesettingsmobs_c = null;
++ GroupDataEntity groupdataentity = null;
++ int j1 = Mth.ceil(level.random.nextFloat() * 4.0F);
+ int k1 = 0;
+ int l1 = 0;
+
+@@ -169,47 +189,52 @@
+ if (l1 < j1) {
+ label53:
+ {
+- l += serverlevel.random.nextInt(6) - serverlevel.random.nextInt(6);
+- i1 += serverlevel.random.nextInt(6) - serverlevel.random.nextInt(6);
+- blockpos_mutableblockpos.set(l, i, i1);
++ l += level.random.nextInt(6) - level.random.nextInt(6);
++ i1 += level.random.nextInt(6) - level.random.nextInt(6);
++ blockposition_mutableblockposition.set(l, i, i1);
+ double d0 = (double) l + 0.5D;
+ double d1 = (double) i1 + 0.5D;
+- Player player = serverlevel.getNearestPlayer(d0, (double) i, d1, -1.0D, false);
++ Player entityhuman = level.getNearestPlayer(d0, (double) i, d1, -1.0D, false);
+
+- if (player != null) {
+- double d2 = player.distanceToSqr(d0, (double) i, d1);
++ if (entityhuman != null) {
++ double d2 = entityhuman.distanceToSqr(d0, (double) i, d1);
+
+- if (isRightDistanceToPlayerAndSpawnPoint(serverlevel, chunkaccess, blockpos_mutableblockpos, d2)) {
+- if (mobspawnsettings_spawnerdata == null) {
+- Optional<MobSpawnSettings.SpawnerData> optional = getRandomSpawnMobAt(serverlevel, structuremanager, chunkgenerator, mobcategory, serverlevel.random, blockpos_mutableblockpos);
++ if (isRightDistanceToPlayerAndSpawnPoint(level, chunk, blockposition_mutableblockposition, d2)) {
++ if (biomesettingsmobs_c == null) {
++ Optional<MobSpawnSettings.SpawnerData> optional = getRandomSpawnMobAt(level, structuremanager, chunkgenerator, category, level.random, blockposition_mutableblockposition);
+
+ if (optional.isEmpty()) {
+ break label53;
+ }
+
+- mobspawnsettings_spawnerdata = (MobSpawnSettings.SpawnerData) optional.get();
+- j1 = mobspawnsettings_spawnerdata.minCount + serverlevel.random.nextInt(1 + mobspawnsettings_spawnerdata.maxCount - mobspawnsettings_spawnerdata.minCount);
++ biomesettingsmobs_c = (MobSpawnSettings.SpawnerData) optional.get();
++ j1 = biomesettingsmobs_c.minCount + level.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
+ }
+
+- if (isValidSpawnPostitionForType(serverlevel, mobcategory, structuremanager, chunkgenerator, mobspawnsettings_spawnerdata, blockpos_mutableblockpos, d2) && naturalspawner_spawnpredicate.test(mobspawnsettings_spawnerdata.type, blockpos_mutableblockpos, chunkaccess)) {
+- Mob mob = getMobForSpawn(serverlevel, mobspawnsettings_spawnerdata.type);
++ if (isValidSpawnPostitionForType(level, category, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && filter.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) {
++ Mob entityinsentient = getMobForSpawn(level, biomesettingsmobs_c.type);
+
+- if (mob == null) {
++ if (entityinsentient == null) {
+ return;
+ }
+
+- mob.moveTo(d0, (double) i, d1, serverlevel.random.nextFloat() * 360.0F, 0.0F);
+- if (isValidPositionForMob(serverlevel, mob, d2)) {
+- spawngroupdata = mob.finalizeSpawn(serverlevel, serverlevel.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.NATURAL, spawngroupdata, (CompoundTag) null);
+- ++j;
+- ++k1;
+- serverlevel.addFreshEntityWithPassengers(mob);
+- naturalspawner_afterspawncallback.run(mob, chunkaccess);
+- if (j >= mob.getMaxSpawnClusterSize()) {
++ entityinsentient.moveTo(d0, (double) i, d1, level.random.nextFloat() * 360.0F, 0.0F);
++ if (isValidPositionForMob(level, entityinsentient, d2)) {
++ groupdataentity = entityinsentient.finalizeSpawn(level, level.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.NATURAL, groupdataentity, (CompoundTag) null);
++ // CraftBukkit start
++ // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs.
++ level.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL);
++ if (!entityinsentient.isRemoved()) {
++ ++j;
++ ++k1;
++ callback.run(entityinsentient, chunk);
++ }
++ // CraftBukkit end
++ if (j >= entityinsentient.getMaxSpawnClusterSize()) {
+ return;
+ }
+
+- if (mob.isMaxGroupSizeReached(k1)) {
++ if (entityinsentient.isMaxGroupSizeReached(k1)) {
+ break label53;
+ }
+ }
+@@ -230,38 +255,38 @@
+ }
+ }
+
+- private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel serverlevel, ChunkAccess chunkaccess, BlockPos.MutableBlockPos blockpos_mutableblockpos, double d0) {
+- return d0 <= 576.0D ? false : (serverlevel.getSharedSpawnPos().closerToCenterThan(new Vec3((double) blockpos_mutableblockpos.getX() + 0.5D, (double) blockpos_mutableblockpos.getY(), (double) blockpos_mutableblockpos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(blockpos_mutableblockpos), chunkaccess.getPos()) || serverlevel.isNaturalSpawningAllowed((BlockPos) blockpos_mutableblockpos));
++ private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel level, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double distance) {
++ return distance <= 576.0D ? false : (level.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || level.isNaturalSpawningAllowed((BlockPos) pos));
+ }
+
+- private static boolean isValidSpawnPostitionForType(ServerLevel serverlevel, MobCategory mobcategory, StructureManager structuremanager, ChunkGenerator chunkgenerator, MobSpawnSettings.SpawnerData mobspawnsettings_spawnerdata, BlockPos.MutableBlockPos blockpos_mutableblockpos, double d0) {
+- EntityType<?> entitytype = mobspawnsettings_spawnerdata.type;
++ private static boolean isValidSpawnPostitionForType(ServerLevel level, MobCategory category, StructureManager structureManager, ChunkGenerator generator, MobSpawnSettings.SpawnerData data, BlockPos.MutableBlockPos pos, double distance) {
++ EntityType<?> entitytypes = data.type;
+
+- if (entitytype.getCategory() == MobCategory.MISC) {
++ if (entitytypes.getCategory() == MobCategory.MISC) {
+ return false;
+- } else if (!entitytype.canSpawnFarFromPlayer() && d0 > (double) (entitytype.getCategory().getDespawnDistance() * entitytype.getCategory().getDespawnDistance())) {
++ } else if (!entitytypes.canSpawnFarFromPlayer() && distance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance())) {
+ return false;
+- } else if (entitytype.canSummon() && canSpawnMobAt(serverlevel, structuremanager, chunkgenerator, mobcategory, mobspawnsettings_spawnerdata, blockpos_mutableblockpos)) {
+- SpawnPlacements.Type spawnplacements_type = SpawnPlacements.getPlacementType(entitytype);
++ } else if (entitytypes.canSummon() && canSpawnMobAt(level, structureManager, generator, category, data, pos)) {
++ SpawnPlacements.Surface entitypositiontypes_surface = SpawnPlacements.getPlacementType(entitytypes);
+
+- return !isSpawnPositionOk(spawnplacements_type, serverlevel, blockpos_mutableblockpos, entitytype) ? false : (!SpawnPlacements.checkSpawnRules(entitytype, serverlevel, MobSpawnType.NATURAL, blockpos_mutableblockpos, serverlevel.random) ? false : serverlevel.noCollision(entitytype.getAABB((double) blockpos_mutableblockpos.getX() + 0.5D, (double) blockpos_mutableblockpos.getY(), (double) blockpos_mutableblockpos.getZ() + 0.5D)));
++ return !isSpawnPositionOk(entitypositiontypes_surface, level, pos, entitytypes) ? false : (!SpawnPlacements.checkSpawnRules(entitytypes, level, EnumMobSpawn.NATURAL, pos, level.random) ? false : level.noCollision(entitytypes.getAABB((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D)));
+ } else {
+ return false;
+ }
+ }
+
+ @Nullable
+- private static Mob getMobForSpawn(ServerLevel serverlevel, EntityType<?> entitytype) {
++ private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) {
+ try {
+- Entity entity = entitytype.create(serverlevel);
++ Entity entity = entityType.create(level);
+
+ if (entity instanceof Mob) {
+- Mob mob = (Mob) entity;
++ Mob entityinsentient = (Mob) entity;
+
+- return mob;
++ return entityinsentient;
+ }
+
+- NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entitytype));
++ NaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType));
+ } catch (Exception exception) {
+ NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
+ }
+@@ -269,90 +294,90 @@
+ return null;
+ }
+
+- private static boolean isValidPositionForMob(ServerLevel serverlevel, Mob mob, double d0) {
+- return d0 > (double) (mob.getType().getCategory().getDespawnDistance() * mob.getType().getCategory().getDespawnDistance()) && mob.removeWhenFarAway(d0) ? false : mob.checkSpawnRules(serverlevel, MobSpawnType.NATURAL) && mob.checkSpawnObstruction(serverlevel);
++ private static boolean isValidPositionForMob(ServerLevel level, Mob mob, double distance) {
++ return distance > (double) (mob.getType().getCategory().getDespawnDistance() * mob.getType().getCategory().getDespawnDistance()) && mob.removeWhenFarAway(distance) ? false : mob.checkSpawnRules(level, EnumMobSpawn.NATURAL) && mob.checkSpawnObstruction(level);
+ }
+
+- private static Optional<MobSpawnSettings.SpawnerData> getRandomSpawnMobAt(ServerLevel serverlevel, StructureManager structuremanager, ChunkGenerator chunkgenerator, MobCategory mobcategory, RandomSource randomsource, BlockPos blockpos) {
+- Holder<Biome> holder = serverlevel.getBiome(blockpos);
++ private static Optional<MobSpawnSettings.SpawnerData> getRandomSpawnMobAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos) {
++ Holder<Biome> holder = level.getBiome(pos);
+
+- return mobcategory == MobCategory.WATER_AMBIENT && holder.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && randomsource.nextFloat() < 0.98F ? Optional.empty() : mobsAt(serverlevel, structuremanager, chunkgenerator, mobcategory, blockpos, holder).getRandom(randomsource);
++ return category == MobCategory.WATER_AMBIENT && holder.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F ? Optional.empty() : mobsAt(level, structureManager, generator, category, pos, holder).getRandom(random);
+ }
+
+- private static boolean canSpawnMobAt(ServerLevel serverlevel, StructureManager structuremanager, ChunkGenerator chunkgenerator, MobCategory mobcategory, MobSpawnSettings.SpawnerData mobspawnsettings_spawnerdata, BlockPos blockpos) {
+- return mobsAt(serverlevel, structuremanager, chunkgenerator, mobcategory, blockpos, (Holder) null).unwrap().contains(mobspawnsettings_spawnerdata);
++ private static boolean canSpawnMobAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos) {
++ return mobsAt(level, structureManager, generator, category, pos, (Holder) null).unwrap().contains(data);
+ }
+
+- private static WeightedRandomList<MobSpawnSettings.SpawnerData> mobsAt(ServerLevel serverlevel, StructureManager structuremanager, ChunkGenerator chunkgenerator, MobCategory mobcategory, BlockPos blockpos, @Nullable Holder<Biome> holder) {
+- return isInNetherFortressBounds(blockpos, serverlevel, mobcategory, structuremanager) ? NetherFortressStructure.FORTRESS_ENEMIES : chunkgenerator.getMobsAt(holder != null ? holder : serverlevel.getBiome(blockpos), structuremanager, mobcategory, blockpos);
++ private static WeightedRandomList<MobSpawnSettings.SpawnerData> mobsAt(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, BlockPos pos, @Nullable Holder<Biome> biome) {
++ return isInNetherFortressBounds(pos, level, category, structureManager) ? NetherFortressStructure.FORTRESS_ENEMIES : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, category, pos);
+ }
+
+- public static boolean isInNetherFortressBounds(BlockPos blockpos, ServerLevel serverlevel, MobCategory mobcategory, StructureManager structuremanager) {
+- if (mobcategory == MobCategory.MONSTER && serverlevel.getBlockState(blockpos.below()).is(Blocks.NETHER_BRICKS)) {
+- Structure structure = (Structure) structuremanager.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS);
++ public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
++ if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) {
++ Structure structure = (Structure) structureManager.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS);
+
+- return structure == null ? false : structuremanager.getStructureAt(blockpos, structure).isValid();
++ return structure == null ? false : structureManager.getStructureAt(pos, structure).isValid();
+ } else {
+ return false;
+ }
+ }
+
+- private static BlockPos getRandomPosWithin(Level level, LevelChunk levelchunk) {
+- ChunkPos chunkpos = levelchunk.getPos();
+- int i = chunkpos.getMinBlockX() + level.random.nextInt(16);
+- int j = chunkpos.getMinBlockZ() + level.random.nextInt(16);
+- int k = levelchunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1;
++ private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
++ ChunkPos chunkcoordintpair = chunk.getPos();
++ int i = chunkcoordintpair.getMinBlockX() + level.random.nextInt(16);
++ int j = chunkcoordintpair.getMinBlockZ() + level.random.nextInt(16);
++ int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1;
+ int l = Mth.randomBetweenInclusive(level.random, level.getMinBuildHeight(), k);
+
+ return new BlockPos(i, l, j);
+ }
+
+- public static boolean isValidEmptySpawnBlock(BlockGetter blockgetter, BlockPos blockpos, BlockState blockstate, FluidState fluidstate, EntityType<?> entitytype) {
+- return blockstate.isCollisionShapeFullBlock(blockgetter, blockpos) ? false : (blockstate.isSignalSource() ? false : (!fluidstate.isEmpty() ? false : (blockstate.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) ? false : !entitytype.isBlockDangerous(blockstate))));
++ public static boolean isValidEmptySpawnBlock(BlockGetter block, BlockPos pos, IBlockData blockState, FluidState fluidState, EntityType<?> entityType) {
++ return blockState.isCollisionShapeFullBlock(block, pos) ? false : (blockState.isSignalSource() ? false : (!fluidState.isEmpty() ? false : (blockState.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) ? false : !entityType.isBlockDangerous(blockState))));
+ }
+
+- public static boolean isSpawnPositionOk(SpawnPlacements.Type spawnplacements_type, LevelReader levelreader, BlockPos blockpos, @Nullable EntityType<?> entitytype) {
+- if (spawnplacements_type == SpawnPlacements.Type.NO_RESTRICTIONS) {
++ public static boolean isSpawnPositionOk(SpawnPlacements.Surface placeType, LevelReader level, BlockPos pos, @Nullable EntityType<?> entityType) {
++ if (placeType == SpawnPlacements.Surface.NO_RESTRICTIONS) {
+ return true;
+- } else if (entitytype != null && levelreader.getWorldBorder().isWithinBounds(blockpos)) {
+- BlockState blockstate = levelreader.getBlockState(blockpos);
+- FluidState fluidstate = levelreader.getFluidState(blockpos);
+- BlockPos blockpos1 = blockpos.above();
+- BlockPos blockpos2 = blockpos.below();
++ } else if (entityType != null && level.getWorldBorder().isWithinBounds(pos)) {
++ IBlockData iblockdata = level.getBlockState(pos);
++ FluidState fluid = level.getFluidState(pos);
++ BlockPos blockposition1 = pos.above();
++ BlockPos blockposition2 = pos.below();
+
+- switch (spawnplacements_type) {
++ switch (placeType) {
+ case IN_WATER:
+- return fluidstate.is(FluidTags.WATER) && !levelreader.getBlockState(blockpos1).isRedstoneConductor(levelreader, blockpos1);
++ return fluid.is(FluidTags.WATER) && !level.getBlockState(blockposition1).isRedstoneConductor(level, blockposition1);
+ case IN_LAVA:
+- return fluidstate.is(FluidTags.LAVA);
++ return fluid.is(FluidTags.LAVA);
+ case ON_GROUND:
+ default:
+- BlockState blockstate1 = levelreader.getBlockState(blockpos2);
++ IBlockData iblockdata1 = level.getBlockState(blockposition2);
+
+- return !blockstate1.isValidSpawn(levelreader, blockpos2, entitytype) ? false : isValidEmptySpawnBlock(levelreader, blockpos, blockstate, fluidstate, entitytype) && isValidEmptySpawnBlock(levelreader, blockpos1, levelreader.getBlockState(blockpos1), levelreader.getFluidState(blockpos1), entitytype);
++ return !iblockdata1.isValidSpawn(level, blockposition2, entityType) ? false : isValidEmptySpawnBlock(level, pos, iblockdata, fluid, entityType) && isValidEmptySpawnBlock(level, blockposition1, level.getBlockState(blockposition1), level.getFluidState(blockposition1), entityType);
+ }
+ } else {
+ return false;
+ }
+ }
+
+- public static void spawnMobsForChunkGeneration(ServerLevelAccessor serverlevelaccessor, Holder<Biome> holder, ChunkPos chunkpos, RandomSource randomsource) {
+- MobSpawnSettings mobspawnsettings = ((Biome) holder.value()).getMobSettings();
+- WeightedRandomList<MobSpawnSettings.SpawnerData> weightedrandomlist = mobspawnsettings.getMobs(MobCategory.CREATURE);
++ public static void spawnMobsForChunkGeneration(ServerLevelAccessor levelAccessor, Holder<Biome> biome, ChunkPos chunkPos, RandomSource random) {
++ MobSpawnSettings biomesettingsmobs = ((Biome) biome.value()).getMobSettings();
++ WeightedRandomList<MobSpawnSettings.SpawnerData> weightedrandomlist = biomesettingsmobs.getMobs(MobCategory.CREATURE);
+
+ if (!weightedrandomlist.isEmpty()) {
+- int i = chunkpos.getMinBlockX();
+- int j = chunkpos.getMinBlockZ();
++ int i = chunkPos.getMinBlockX();
++ int j = chunkPos.getMinBlockZ();
+
+- while (randomsource.nextFloat() < mobspawnsettings.getCreatureProbability()) {
+- Optional<MobSpawnSettings.SpawnerData> optional = weightedrandomlist.getRandom(randomsource);
++ while (random.nextFloat() < biomesettingsmobs.getCreatureProbability()) {
++ Optional<MobSpawnSettings.SpawnerData> optional = weightedrandomlist.getRandom(random);
+
+ if (!optional.isEmpty()) {
+- MobSpawnSettings.SpawnerData mobspawnsettings_spawnerdata = (MobSpawnSettings.SpawnerData) optional.get();
+- int k = mobspawnsettings_spawnerdata.minCount + randomsource.nextInt(1 + mobspawnsettings_spawnerdata.maxCount - mobspawnsettings_spawnerdata.minCount);
+- SpawnGroupData spawngroupdata = null;
+- int l = i + randomsource.nextInt(16);
+- int i1 = j + randomsource.nextInt(16);
++ MobSpawnSettings.SpawnerData biomesettingsmobs_c = (MobSpawnSettings.SpawnerData) optional.get();
++ int k = biomesettingsmobs_c.minCount + random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount);
++ GroupDataEntity groupdataentity = null;
++ int l = i + random.nextInt(16);
++ int i1 = j + random.nextInt(16);
+ int j1 = l;
+ int k1 = i1;
+
+@@ -360,21 +385,21 @@
+ boolean flag = false;
+
+ for (int i2 = 0; !flag && i2 < 4; ++i2) {
+- BlockPos blockpos = getTopNonCollidingPos(serverlevelaccessor, mobspawnsettings_spawnerdata.type, l, i1);
++ BlockPos blockposition = getTopNonCollidingPos(levelAccessor, biomesettingsmobs_c.type, l, i1);
+
+- if (mobspawnsettings_spawnerdata.type.canSummon() && isSpawnPositionOk(SpawnPlacements.getPlacementType(mobspawnsettings_spawnerdata.type), serverlevelaccessor, blockpos, mobspawnsettings_spawnerdata.type)) {
+- float f = mobspawnsettings_spawnerdata.type.getWidth();
++ if (biomesettingsmobs_c.type.canSummon() && isSpawnPositionOk(SpawnPlacements.getPlacementType(biomesettingsmobs_c.type), levelAccessor, blockposition, biomesettingsmobs_c.type)) {
++ float f = biomesettingsmobs_c.type.getWidth();
+ double d0 = Mth.clamp((double) l, (double) i + (double) f, (double) i + 16.0D - (double) f);
+ double d1 = Mth.clamp((double) i1, (double) j + (double) f, (double) j + 16.0D - (double) f);
+
+- if (!serverlevelaccessor.noCollision(mobspawnsettings_spawnerdata.type.getAABB(d0, (double) blockpos.getY(), d1)) || !SpawnPlacements.checkSpawnRules(mobspawnsettings_spawnerdata.type, serverlevelaccessor, MobSpawnType.CHUNK_GENERATION, BlockPos.containing(d0, (double) blockpos.getY(), d1), serverlevelaccessor.getRandom())) {
++ if (!levelAccessor.noCollision(biomesettingsmobs_c.type.getAABB(d0, (double) blockposition.getY(), d1)) || !SpawnPlacements.checkSpawnRules(biomesettingsmobs_c.type, levelAccessor, EnumMobSpawn.CHUNK_GENERATION, BlockPos.containing(d0, (double) blockposition.getY(), d1), levelAccessor.getRandom())) {
+ continue;
+ }
+
+ Entity entity;
+
+ try {
+- entity = mobspawnsettings_spawnerdata.type.create(serverlevelaccessor.getLevel());
++ entity = biomesettingsmobs_c.type.create(levelAccessor.getLevel());
+ } catch (Exception exception) {
+ NaturalSpawner.LOGGER.warn("Failed to create mob", exception);
+ continue;
+@@ -384,22 +409,22 @@
+ continue;
+ }
+
+- entity.moveTo(d0, (double) blockpos.getY(), d1, randomsource.nextFloat() * 360.0F, 0.0F);
++ entity.moveTo(d0, (double) blockposition.getY(), d1, random.nextFloat() * 360.0F, 0.0F);
+ if (entity instanceof Mob) {
+- Mob mob = (Mob) entity;
++ Mob entityinsentient = (Mob) entity;
+
+- if (mob.checkSpawnRules(serverlevelaccessor, MobSpawnType.CHUNK_GENERATION) && mob.checkSpawnObstruction(serverlevelaccessor)) {
+- spawngroupdata = mob.finalizeSpawn(serverlevelaccessor, serverlevelaccessor.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.CHUNK_GENERATION, spawngroupdata, (CompoundTag) null);
+- serverlevelaccessor.addFreshEntityWithPassengers(mob);
++ if (entityinsentient.checkSpawnRules(levelAccessor, EnumMobSpawn.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(levelAccessor)) {
++ groupdataentity = entityinsentient.finalizeSpawn(levelAccessor, levelAccessor.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.CHUNK_GENERATION, groupdataentity, (CompoundTag) null);
++ levelAccessor.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit
+ flag = true;
+ }
+ }
+ }
+
+- l += randomsource.nextInt(5) - randomsource.nextInt(5);
++ l += random.nextInt(5) - random.nextInt(5);
+
+- for (i1 += randomsource.nextInt(5) - randomsource.nextInt(5); l < i || l >= i + 16 || i1 < j || i1 >= j + 16; i1 = k1 + randomsource.nextInt(5) - randomsource.nextInt(5)) {
+- l = j1 + randomsource.nextInt(5) - randomsource.nextInt(5);
++ for (i1 += random.nextInt(5) - random.nextInt(5); l < i || l >= i + 16 || i1 < j || i1 >= j + 16; i1 = k1 + random.nextInt(5) - random.nextInt(5)) {
++ l = j1 + random.nextInt(5) - random.nextInt(5);
+ }
+ }
+ }
+@@ -409,29 +434,29 @@
+ }
+ }
+
+- private static BlockPos getTopNonCollidingPos(LevelReader levelreader, EntityType<?> entitytype, int i, int j) {
+- int k = levelreader.getHeight(SpawnPlacements.getHeightmapType(entitytype), i, j);
+- BlockPos.MutableBlockPos blockpos_mutableblockpos = new BlockPos.MutableBlockPos(i, k, j);
++ private static BlockPos getTopNonCollidingPos(LevelReader level, EntityType<?> entityType, int x, int z) {
++ int k = level.getHeight(SpawnPlacements.getHeightmapType(entityType), x, z);
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, k, z);
+
+- if (levelreader.dimensionType().hasCeiling()) {
++ if (level.dimensionType().hasCeiling()) {
+ do {
+- blockpos_mutableblockpos.move(Direction.DOWN);
+- } while (!levelreader.getBlockState(blockpos_mutableblockpos).isAir());
++ blockposition_mutableblockposition.move(Direction.DOWN);
++ } while (!level.getBlockState(blockposition_mutableblockposition).isAir());
+
+ do {
+- blockpos_mutableblockpos.move(Direction.DOWN);
+- } while (levelreader.getBlockState(blockpos_mutableblockpos).isAir() && blockpos_mutableblockpos.getY() > levelreader.getMinBuildHeight());
++ blockposition_mutableblockposition.move(Direction.DOWN);
++ } while (level.getBlockState(blockposition_mutableblockposition).isAir() && blockposition_mutableblockposition.getY() > level.getMinBuildHeight());
+ }
+
+- if (SpawnPlacements.getPlacementType(entitytype) == SpawnPlacements.Type.ON_GROUND) {
+- BlockPos blockpos = blockpos_mutableblockpos.below();
++ if (SpawnPlacements.getPlacementType(entityType) == SpawnPlacements.Surface.ON_GROUND) {
++ BlockPos blockposition = blockposition_mutableblockposition.below();
+
+- if (levelreader.getBlockState(blockpos).isPathfindable(levelreader, blockpos, PathComputationType.LAND)) {
+- return blockpos;
++ if (level.getBlockState(blockposition).isPathfindable(level, blockposition, PathMode.LAND)) {
++ return blockposition;
+ }
+ }
+
+- return blockpos_mutableblockpos.immutable();
++ return blockposition_mutableblockposition.immutable();
+ }
+
+ @FunctionalInterface
+@@ -453,54 +478,54 @@
+ private EntityType<?> lastCheckedType;
+ private double lastCharge;
+
+- SpawnState(int i, Object2IntOpenHashMap<MobCategory> object2intopenhashmap, PotentialCalculator potentialcalculator, LocalMobCapCalculator localmobcapcalculator) {
+- this.spawnableChunkCount = i;
+- this.mobCategoryCounts = object2intopenhashmap;
+- this.spawnPotential = potentialcalculator;
+- this.localMobCapCalculator = localmobcapcalculator;
+- this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(object2intopenhashmap);
++ SpawnState(int spawnableChunkCount, Object2IntOpenHashMap<MobCategory> mobCategoryCounts, PotentialCalculator spawnPotential, LocalMobCapCalculator localMobCapCalculator) {
++ this.spawnableChunkCount = spawnableChunkCount;
++ this.mobCategoryCounts = mobCategoryCounts;
++ this.spawnPotential = spawnPotential;
++ this.localMobCapCalculator = localMobCapCalculator;
++ this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
+ }
+
+- private boolean canSpawn(EntityType<?> entitytype, BlockPos blockpos, ChunkAccess chunkaccess) {
+- this.lastCheckedPos = blockpos;
+- this.lastCheckedType = entitytype;
+- MobSpawnSettings.MobSpawnCost mobspawnsettings_mobspawncost = NaturalSpawner.getRoughBiome(blockpos, chunkaccess).getMobSettings().getMobSpawnCost(entitytype);
++ private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) {
++ this.lastCheckedPos = pos;
++ this.lastCheckedType = entityType;
++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(entityType);
+
+- if (mobspawnsettings_mobspawncost == null) {
++ if (biomesettingsmobs_b == null) {
+ this.lastCharge = 0.0D;
+ return true;
+ } else {
+- double d0 = mobspawnsettings_mobspawncost.charge();
++ double d0 = biomesettingsmobs_b.charge();
+
+ this.lastCharge = d0;
+- double d1 = this.spawnPotential.getPotentialEnergyChange(blockpos, d0);
++ double d1 = this.spawnPotential.getPotentialEnergyChange(pos, d0);
+
+- return d1 <= mobspawnsettings_mobspawncost.energyBudget();
++ return d1 <= biomesettingsmobs_b.energyBudget();
+ }
+ }
+
+- private void afterSpawn(Mob mob, ChunkAccess chunkaccess) {
+- EntityType<?> entitytype = mob.getType();
+- BlockPos blockpos = mob.blockPosition();
++ private void afterSpawn(Mob mob, ChunkAccess chunk) {
++ EntityType<?> entitytypes = mob.getType();
++ BlockPos blockposition = mob.blockPosition();
+ double d0;
+
+- if (blockpos.equals(this.lastCheckedPos) && entitytype == this.lastCheckedType) {
++ if (blockposition.equals(this.lastCheckedPos) && entitytypes == this.lastCheckedType) {
+ d0 = this.lastCharge;
+ } else {
+- MobSpawnSettings.MobSpawnCost mobspawnsettings_mobspawncost = NaturalSpawner.getRoughBiome(blockpos, chunkaccess).getMobSettings().getMobSpawnCost(entitytype);
++ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entitytypes);
+
+- if (mobspawnsettings_mobspawncost != null) {
+- d0 = mobspawnsettings_mobspawncost.charge();
++ if (biomesettingsmobs_b != null) {
++ d0 = biomesettingsmobs_b.charge();
+ } else {
+ d0 = 0.0D;
+ }
+ }
+
+- this.spawnPotential.addCharge(blockpos, d0);
+- MobCategory mobcategory = entitytype.getCategory();
++ this.spawnPotential.addCharge(blockposition, d0);
++ MobCategory enumcreaturetype = entitytypes.getCategory();
+
+- this.mobCategoryCounts.addTo(mobcategory, 1);
+- this.localMobCapCalculator.addMob(new ChunkPos(blockpos), mobcategory);
++ this.mobCategoryCounts.addTo(enumcreaturetype, 1);
++ this.localMobCapCalculator.addMob(new ChunkPos(blockposition), enumcreaturetype);
+ }
+
+ public int getSpawnableChunkCount() {
+@@ -511,10 +536,12 @@
+ return this.unmodifiableMobCategoryCounts;
+ }
+
+- boolean canSpawnForCategory(MobCategory mobcategory, ChunkPos chunkpos) {
+- int i = mobcategory.getMaxInstancesPerChunk() * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
++ // CraftBukkit start
++ boolean canSpawnForCategory(MobCategory enumcreaturetype, ChunkPos chunkcoordintpair, int limit) {
++ int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
++ // CraftBukkit end
+
+- return this.mobCategoryCounts.getInt(mobcategory) >= i ? false : this.localMobCapCalculator.canSpawn(mobcategory, chunkpos);
++ return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair);
+ }
+ }
+