From 22baa6be4daa8a11770411b50f1fbf545196407d Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Wed, 2 Mar 2016 14:35:27 -0600
Subject: [PATCH] Add player view distance API


diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java
index 58e037e13b..e97bb2305c 100644
--- a/src/main/java/net/minecraft/server/EntityHuman.java
+++ b/src/main/java/net/minecraft/server/EntityHuman.java
@@ -71,6 +71,15 @@ public abstract class EntityHuman extends EntityLiving {
     // Paper start
     public boolean affectsSpawning = true;
     // Paper end
+    // Paper start - Player view distance API
+    private int viewDistance = -1;
+    public int getViewDistance() {
+        return viewDistance == -1 ? ((WorldServer) world).getPlayerChunkMap().getViewDistance() : viewDistance;
+    }
+    public void setViewDistance(int viewDistance) {
+        this.viewDistance = viewDistance;
+    }
+    // Paper end
 
     // CraftBukkit start
     public boolean fauxSleeping;
diff --git a/src/main/java/net/minecraft/server/EntityTracker.java b/src/main/java/net/minecraft/server/EntityTracker.java
index 45ab33d1ae..3854ae9769 100644
--- a/src/main/java/net/minecraft/server/EntityTracker.java
+++ b/src/main/java/net/minecraft/server/EntityTracker.java
@@ -200,6 +200,7 @@ public class EntityTracker {
 
     }
 
+    public void updatePlayer(EntityPlayer entityplayer) { a(entityplayer); } // Paper - OBFHELPER
     public void a(EntityPlayer entityplayer) {
         Iterator iterator = this.c.iterator();
 
diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
index d00401ce14..dd6c84b4a2 100644
--- a/src/main/java/net/minecraft/server/EntityTrackerEntry.java
+++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java
@@ -435,7 +435,7 @@ public class EntityTrackerEntry {
     public boolean c(EntityPlayer entityplayer) {
         double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D;
         double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D;
-        int i = Math.min(this.e, this.f);
+        int i = Math.min(this.e, (entityplayer.getViewDistance() - 1) * 16); // Paper - Use player view distance API
 
         return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer);
     }
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
index e01222ad2b..55161af9c9 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -33,7 +33,7 @@ public class PlayerChunkMap {
     private final List<PlayerChunk> g = Lists.newLinkedList();
     private final List<PlayerChunk> h = Lists.newLinkedList();
     private final List<PlayerChunk> i = Lists.newArrayList();
-    private int j;
+    private int j;public int getViewDistance() { return j; } // Paper OBFHELPER
     private long k;
     private boolean l = true;
     private boolean m = true;
@@ -257,8 +257,11 @@ public class PlayerChunkMap {
         // CraftBukkit start - Load nearby chunks first
         List<ChunkCoordIntPair> chunkList = new LinkedList<ChunkCoordIntPair>();
 
-        for (int k = i - this.j; k <= i + this.j; ++k) {
-            for (int l = j - this.j; l <= j + this.j; ++l) {
+        // Paper start - Player view distance API
+        int viewDistance = entityplayer.getViewDistance();
+        for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
+            for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
+                // Paper end
                 chunkList.add(new ChunkCoordIntPair(k, l));
             }
         }
@@ -277,8 +280,11 @@ public class PlayerChunkMap {
         int i = (int) entityplayer.d >> 4;
         int j = (int) entityplayer.e >> 4;
 
-        for (int k = i - this.j; k <= i + this.j; ++k) {
-            for (int l = j - this.j; l <= j + this.j; ++l) {
+        // Paper start - Player view distance API
+        int viewDistance = entityplayer.getViewDistance();
+        for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
+            for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
+                // Paper end
                 PlayerChunk playerchunk = this.getChunk(k, l);
 
                 if (playerchunk != null) {
@@ -308,7 +314,8 @@ public class PlayerChunkMap {
         if (d2 >= 64.0D) {
             int k = (int) entityplayer.d >> 4;
             int l = (int) entityplayer.e >> 4;
-            int i1 = this.j;
+            int i1 = entityplayer.getViewDistance(); // Paper - Player view distance API
+
             int j1 = i - k;
             int k1 = j - l;
 
@@ -352,6 +359,8 @@ public class PlayerChunkMap {
         return playerchunk != null && playerchunk.d(entityplayer) && playerchunk.e();
     }
 
+    public final void setViewDistanceForAll(int viewDistance) { this.a(viewDistance); } // Paper - OBFHELPER
+    // Paper start - Separate into two methods
     public void a(int i) {
         i = MathHelper.clamp(i, 3, 32);
         if (i != this.j) {
@@ -361,36 +370,55 @@ public class PlayerChunkMap {
 
             while (iterator.hasNext()) {
                 EntityPlayer entityplayer = (EntityPlayer) iterator.next();
-                int k = (int) entityplayer.locX >> 4;
-                int l = (int) entityplayer.locZ >> 4;
-                int i1;
-                int j1;
-
-                if (j > 0) {
-                    for (i1 = k - i; i1 <= k + i; ++i1) {
-                        for (j1 = l - i; j1 <= l + i; ++j1) {
-                            PlayerChunk playerchunk = this.c(i1, j1);
-
-                            if (!playerchunk.d(entityplayer)) {
-                                playerchunk.a(entityplayer);
-                            }
+                this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after
+            }
+
+            this.j = i;
+            this.e();
+        }
+    }
+
+    public void setViewDistance(EntityPlayer entityplayer, int i) {
+        this.setViewDistance(entityplayer, i, true); // Mark sort pending by default so we don't have to remember to do so all the time
+    }
+    
+    // Copied from above with minor changes
+    public void setViewDistance(EntityPlayer entityplayer, int i, boolean markSort) {
+        i = MathHelper.clamp(i, 3, 32);
+        int oldViewDistance = entityplayer.getViewDistance();
+        if (i != oldViewDistance) {
+            int j = i - oldViewDistance;
+            
+            int k = (int) entityplayer.locX >> 4;
+            int l = (int) entityplayer.locZ >> 4;
+            int i1;
+            int j1;
+
+            if (j > 0) {
+                for (i1 = k - i; i1 <= k + i; ++i1) {
+                    for (j1 = l - i; j1 <= l + i; ++j1) {
+                        PlayerChunk playerchunk = this.c(i1, j1);
+
+                        if (!playerchunk.d(entityplayer)) {
+                            playerchunk.a(entityplayer);
                         }
                     }
-                } else {
-                    for (i1 = k - this.j; i1 <= k + this.j; ++i1) {
-                        for (j1 = l - this.j; j1 <= l + this.j; ++j1) {
-                            if (!this.a(i1, j1, k, l, i)) {
-                                this.c(i1, j1).b(entityplayer);
-                            }
+                }
+            } else {
+                for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) {
+                    for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) {
+                        if (!this.a(i1, j1, k, l, i)) {
+                            this.c(i1, j1).b(entityplayer);
                         }
                     }
                 }
+                if (markSort) {
+                    this.e();
+                }
             }
-
-            this.j = i;
-            this.e();
         }
     }
+    // Paper end
 
     private void e() {
         this.l = true;
@@ -469,4 +497,32 @@ public class PlayerChunkMap {
         }
     }
     // CraftBukkit end
+
+    // Paper start - Player view distance API
+    public void updateViewDistance(EntityPlayer player, int distanceIn) {
+        final int oldViewDistance = player.getViewDistance();
+
+        // This represents the view distance that we will set on the player
+        // It can exist as a negative value
+        int playerViewDistance = MathHelper.clamp(distanceIn, 3, 32);
+
+        // This value is the one we actually use to update the chunk map
+        // We don't ever want this to be a negative
+        int toSet = playerViewDistance;
+
+        if (distanceIn < 0) {
+            playerViewDistance = -1;
+            toSet = world.getPlayerChunkMap().getViewDistance();
+        }
+
+        if (toSet != oldViewDistance) {
+            // Order matters
+            this.setViewDistance(player, toSet);
+            player.setViewDistance(playerViewDistance);
+
+            //Force update entity trackers
+            this.getWorld().getTracker().updatePlayer(player);
+        }
+    }
+    // Paper end
 }
diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java
index 4eaa5d93b4..6720a9648e 100644
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
@@ -44,7 +44,7 @@ public final class SpawnerCreature {
                     boolean flag3 = true;
                     // Spigot Start
                     byte b0 = worldserver.spigotConfig.mobSpawnRange;
-                    b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0;
+                    b0 = ( b0 > entityhuman.getViewDistance() ) ? (byte) entityhuman.getViewDistance() : b0; // Paper - Use player view distance API
                     b0 = ( b0 > 8 ) ? 8 : b0;
 
                     for (int i1 = -b0; i1 <= b0; ++i1) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 0ee063bcd3..5496fae409 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1626,6 +1626,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     public boolean getAffectsSpawning() {
         return this.getHandle().affectsSpawning;
     }
+
+    @Override
+    public int getViewDistance() {
+        return getHandle().getViewDistance();
+    }
+
+    @Override
+    public void setViewDistance(int viewDistance) {
+        ((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance);
+    }
     // Paper end
 
     @Override
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index a95f93eb76..09df00e94b 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -108,13 +108,13 @@ public class ActivationRange
 
         int maxRange = Math.max( monsterActivationRange, animalActivationRange );
         maxRange = Math.max( maxRange, miscActivationRange );
-        maxRange = Math.min( ( world.spigotConfig.viewDistance << 4 ) - 8, maxRange );
+        //maxRange = Math.min( ( world.spigotConfig.viewDistance << 4 ) - 8, maxRange ); Paper - Use player view distance API below instead
 
         for ( EntityHuman player : world.players )
         {
-
+            int playerMaxRange = maxRange = Math.min( ( player.getViewDistance() << 4 ) - 8, maxRange ); // Paper - Use player view distance API
             player.activatedTick = MinecraftServer.currentTick;
-            maxBB = player.getBoundingBox().grow( maxRange, 256, maxRange );
+            maxBB = player.getBoundingBox().grow( playerMaxRange, 256, playerMaxRange ); // Paper - Use player view distance API
             miscBB = player.getBoundingBox().grow( miscActivationRange, 256, miscActivationRange );
             animalBB = player.getBoundingBox().grow( animalActivationRange, 256, animalActivationRange );
             monsterBB = player.getBoundingBox().grow( monsterActivationRange, 256, monsterActivationRange );
-- 
2.21.0