From e7f6cff0b4ca05814f3d4bbb173e748ba30abce2 Mon Sep 17 00:00:00 2001
From: md_5 <md_5@live.com.au>
Date: Fri, 21 Jun 2013 17:29:54 +1000
Subject: [PATCH] Fix Mob Spawning Relative to View Distance

Changes the mob spawning algorithm to properly account for view distance and the range around players.

Needs better documentation.

diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 867caca..9e65ff7 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -42,6 +42,7 @@ public class Chunk {
     private long u;
     private int v;
     private ConcurrentLinkedQueue w;
+    protected gnu.trove.map.hash.TObjectIntHashMap<Class> entityCount = new gnu.trove.map.hash.TObjectIntHashMap<Class>(); // Spigot
 
     // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
     private int neighbors = 0x1 << 12;
@@ -650,6 +651,22 @@ public class Chunk {
         entity.af = k;
         entity.ag = this.locZ;
         this.entitySlices[k].add(entity);
+        // Spigot start - increment creature type count
+        // Keep this synced up with World.a(Class)
+        if (entity instanceof EntityInsentient) {
+            EntityInsentient entityinsentient = (EntityInsentient) entity;
+            if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) {
+                return;
+            }
+        }
+        for ( EnumCreatureType creatureType : EnumCreatureType.values() )
+        {
+            if ( creatureType.a().isAssignableFrom( entity.getClass() ) )
+            {
+                this.entityCount.adjustOrPutValue( creatureType.a(), 1, 1 );
+            }
+        }
+        // Spigot end
     }
 
     public void b(Entity entity) {
@@ -666,6 +683,22 @@ public class Chunk {
         }
 
         this.entitySlices[i].remove(entity);
+        // Spigot start - decrement creature type count
+        // Keep this synced up with World.a(Class)
+        if (entity instanceof EntityInsentient) {
+            EntityInsentient entityinsentient = (EntityInsentient) entity;
+            if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) {
+                return;
+            }
+        }
+        for ( EnumCreatureType creatureType : EnumCreatureType.values() )
+        {
+            if ( creatureType.a().isAssignableFrom( entity.getClass() ) )
+            {
+                this.entityCount.adjustValue( creatureType.a(), -1 );
+            }
+        }
+        // Spigot end
     }
 
     public boolean d(BlockPosition blockposition) {
diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java
index 25988d4..43954b3 100644
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
@@ -19,6 +19,25 @@ public final class SpawnerCreature {
 
     public SpawnerCreature() {}
 
+    // Spigot start - get entity count only from chunks being processed in b
+    private int getEntityCount(WorldServer server, Class oClass)
+    {
+        int i = 0;
+        Iterator<Long> it = this.b.iterator();
+        while ( it.hasNext() )
+        {
+            Long coord = it.next();
+            int x = LongHash.msw( coord );
+            int z = LongHash.lsw( coord );
+            if ( !server.chunkProviderServer.unloadQueue.contains( coord ) && server.isChunkLoaded( x, z, true ) )
+            {
+                i += server.getChunkAt( x, z ).entityCount.get( oClass );
+            }
+        }
+        return i;
+    }
+    // Spigot end
+
     public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) {
         if (!flag && !flag1) {
             return 0;
@@ -38,6 +57,11 @@ public final class SpawnerCreature {
 
                     j = MathHelper.floor(entityhuman.locZ / 16.0D);
                     byte b0 = 8;
+                    // Spigot Start
+                    b0 = worldserver.spigotConfig.mobSpawnRange;
+                    b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0;
+                    b0 = ( b0 > 8 ) ? 8 : b0;
+                    // Spigot End
 
                     for (int i1 = -b0; i1 <= b0; ++i1) {
                         for (k = -b0; k <= b0; ++k) {
@@ -88,17 +112,19 @@ public final class SpawnerCreature {
                 if (limit == 0) {
                     continue;
                 }
+				int mobcnt = 0; // Spigot
                 // CraftBukkit end
 
                 if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) {
                     k = worldserver.a(enumcreaturetype.a());
                     int l1 = limit * i / a; // CraftBukkit - use per-world limits
 
-                    if (k <= l1) {
+                    if ((mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * i / 256) {
                         Iterator iterator1 = this.b.iterator();
 
+                        int moblimit = (limit * i / 256) - mobcnt + 1; // Spigot - up to 1 more than limit
                         label115:
-                        while (iterator1.hasNext()) {
+                        while (iterator1.hasNext() && (moblimit > 0)) { // Spigot - while more allowed
                             // CraftBukkit start = use LongHash and LongObjectHashMap
                             long key = ((Long) iterator1.next()).longValue();
                             BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key));
@@ -156,6 +182,13 @@ public final class SpawnerCreature {
                                                                 worldserver.addEntity(entityinsentient, SpawnReason.NATURAL); // CraftBukkit - Added a reason for spawning this creature
                                                             }
 
+                                                            // Spigot start
+                                                            if ( --moblimit <= 0 )
+                                                            {
+                                                                // If we're past limit, stop spawn
+                                                                continue label115;
+                                                            }
+                                                            // Spigot end
                                                             if (l2 >= entityinsentient.bU()) {
                                                                 continue label115;
                                                             }
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 6cc3a91..46249d7 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -131,4 +131,11 @@ public class SpigotWorldConfig
         viewDistance = getInt( "view-distance", Bukkit.getViewDistance() );
         log( "View Distance: " + viewDistance );
     }
+
+    public byte mobSpawnRange;
+    private void mobSpawnRange()
+    {
+        mobSpawnRange = (byte) getInt( "mob-spawn-range", 4 );
+        log( "Mob Spawn Range: " + mobSpawnRange );
+    }
 }
-- 
2.1.0