--- 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<EnumCreatureType> getFilteredSpawningCategories(SpawnerCreature.d spawnercreature_d, boolean flag, boolean flag1, boolean flag2) {
+    // CraftBukkit start - add server
+    public static List<EnumCreatureType> 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<EnumCreatureType> 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;
         }
