--- a/net/minecraft/world/level/SpawnerCreature.java +++ b/net/minecraft/world/level/SpawnerCreature.java @@ -50,6 +50,13 @@ import net.minecraft.world.phys.Vec3D; import org.slf4j.Logger; +// CraftBukkit start +import net.minecraft.world.level.storage.WorldData; +import org.bukkit.craftbukkit.util.CraftSpawnCategory; +import org.bukkit.entity.SpawnCategory; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + public final class SpawnerCreature { private static final Logger LOGGER = LogUtils.getLogger(); @@ -107,15 +114,31 @@ return (BiomeBase) ichunkaccess.getNoiseBiome(QuartPos.fromBlock(blockposition.getX()), QuartPos.fromBlock(blockposition.getY()), QuartPos.fromBlock(blockposition.getZ())).value(); } - public static List getFilteredSpawningCategories(SpawnerCreature.d spawnercreature_d, boolean flag, boolean flag1, boolean flag2) { + // CraftBukkit start - add server + public static List getFilteredSpawningCategories(SpawnerCreature.d spawnercreature_d, boolean flag, boolean flag1, boolean flag2, WorldServer worldserver) { + WorldData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate + // CraftBukkit end List list = new ArrayList(SpawnerCreature.SPAWNING_CATEGORIES.length); EnumCreatureType[] aenumcreaturetype = SpawnerCreature.SPAWNING_CATEGORIES; int i = aenumcreaturetype.length; for (int j = 0; j < i; ++j) { EnumCreatureType 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 = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0; + limit = worldserver.getWorld().getSpawnLimit(spawnCategory); + } - if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype)) { + if (!spawnThisTick || limit == 0) { + continue; + } + + if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategoryGlobal(enumcreaturetype, limit)) { + // CraftBukkit end list.add(enumcreaturetype); } } @@ -217,10 +240,15 @@ entityinsentient.moveTo(d0, (double) i, d1, worldserver.random.nextFloat() * 360.0F, 0.0F); if (isValidPositionForMob(worldserver, entityinsentient, d2)) { groupdataentity = entityinsentient.finalizeSpawn(worldserver, worldserver.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.NATURAL, groupdataentity); - ++j; - ++k1; - worldserver.addFreshEntityWithPassengers(entityinsentient); - spawnercreature_a.run(entityinsentient, ichunkaccess); + // 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. + worldserver.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.EntityOcelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL); + if (!entityinsentient.isRemoved()) { + ++j; + ++k1; + spawnercreature_a.run(entityinsentient, ichunkaccess); + } + // CraftBukkit end if (j >= entityinsentient.getMaxSpawnClusterSize()) { return; } @@ -369,7 +397,7 @@ if (entityinsentient.checkSpawnRules(worldaccess, EntitySpawnReason.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(worldaccess)) { groupdataentity = entityinsentient.finalizeSpawn(worldaccess, worldaccess.getCurrentDifficultyAt(entityinsentient.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupdataentity); - worldaccess.addFreshEntityWithPassengers(entityinsentient); + worldaccess.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit flag = true; } } @@ -482,8 +510,10 @@ return this.unmodifiableMobCategoryCounts; } - boolean canSpawnForCategoryGlobal(EnumCreatureType enumcreaturetype) { - int i = enumcreaturetype.getMaxInstancesPerChunk() * this.spawnableChunkCount / SpawnerCreature.MAGIC_NUMBER; + // CraftBukkit start + boolean canSpawnForCategoryGlobal(EnumCreatureType enumcreaturetype, int limit) { + int i = limit * this.spawnableChunkCount / SpawnerCreature.MAGIC_NUMBER; + // CraftBukkit end return this.mobCategoryCounts.getInt(enumcreaturetype) < i; }