From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 02:37:57 -0400
Subject: [PATCH] Optimize Collision to not load chunks

The collision code takes an AABB and generates a cuboid of checks rather
than a cylinder, so at high velocity this can generate a lot of chunk checks.

Treat an unloaded chunk as a collision for entities, and also for players if
the "prevent moving into unloaded chunks" setting is enabled.

If that serting is not enabled, collisions will be ignored for players, since
movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.

diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 7434f859f7f9acff0f881ff594c8dffdfa249c76..7fb34a86dc35cb4bf51e1ce5220e56642096fece 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -80,6 +80,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
     private CraftEntity bukkitEntity;
 
     PlayerChunkMap.EntityTracker tracker; // Paper
+    boolean collisionLoadChunks = false; // Paper
     Throwable addedToWorldStack; // Paper - entity debug
     public CraftEntity getBukkitEntity() {
         if (bukkitEntity == null) {
diff --git a/src/main/java/net/minecraft/server/ICollisionAccess.java b/src/main/java/net/minecraft/server/ICollisionAccess.java
index f851ed11df14fd9aa8017f44d82fb6cfc3bde345..667e6c8f94162b620a7157c7d3caba9369c37de1 100644
--- a/src/main/java/net/minecraft/server/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/server/ICollisionAccess.java
@@ -39,7 +39,12 @@ public interface ICollisionAccess extends IBlockAccess {
     }
 
     default boolean getCubes(Entity entity, AxisAlignedBB axisalignedbb) {
-        return this.a(entity, axisalignedbb, Collections.emptySet());
+        // Paper start - load chunks for getCubes
+        entity.collisionLoadChunks = true;
+        boolean result = this.a(entity, axisalignedbb, Collections.emptySet());
+        entity.collisionLoadChunks = false;
+        return result;
+        // Paper end
     }
 
     default boolean a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set<Entity> set) {
@@ -83,19 +88,33 @@ public interface ICollisionAccess extends IBlockAccess {
                 }
 
                 while (cursorposition.a()) {
-                    int k1 = cursorposition.b();
-                    int l1 = cursorposition.c();
-                    int i2 = cursorposition.d();
+                    int k1 = cursorposition.b();int x = k1; // Paper
+                    int l1 = cursorposition.c();int y = l1; // Paper
+                    int i2 = cursorposition.d();int z = i2; // Paper
                     int j2 = cursorposition.e();
 
                     if (j2 != 3) {
-                        int k2 = k1 >> 4;
-                        int l2 = i2 >> 4;
-                        IBlockAccess iblockaccess = ICollisionAccess.this.c(k2, l2);
-
-                        if (iblockaccess != null) {
-                            blockposition_mutableblockposition.d(k1, l1, i2);
-                            IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
+                        // Paper start - ensure we don't load chunks
+                        //int k2 = k1 >> 4;
+                        //int l2 = i2 >> 4;
+                        boolean far = entity != null && MCUtil.distanceSq(entity.locX(), y, entity.locZ(), x, y, z) > 14;
+                        blockposition_mutableblockposition.setValues(x, y, z);
+
+                        boolean isRegionLimited = ICollisionAccess.this instanceof RegionLimitedWorldAccess;
+                        IBlockData iblockdata = isRegionLimited ? Blocks.VOID_AIR.getBlockData() : ((!far && entity instanceof EntityPlayer) || (entity != null && entity.collisionLoadChunks)
+                            ? ICollisionAccess.this.getType(blockposition_mutableblockposition)
+                            : ICollisionAccess.this.getTypeIfLoaded(blockposition_mutableblockposition)
+                        );
+                        if (iblockdata == null) {
+                            if (!(entity instanceof EntityPlayer) || entity.world.paperConfig.preventMovingIntoUnloadedChunks) {
+                                VoxelShape voxelshape3 = VoxelShapes.of(far ? entity.getBoundingBox() : new AxisAlignedBB(new BlockPosition(x, y, z)));
+                                consumer.accept(voxelshape3);
+                                return true;
+                            }
+                        } else {
+                            //blockposition_mutableblockposition.d(k1, l1, i2); // moved up
+                            //IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); // moved up
+                            // Paper end
 
                             if ((j2 != 1 || iblockdata.f()) && (j2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
                                 VoxelShape voxelshape2 = iblockdata.b((IBlockAccess) ICollisionAccess.this, blockposition_mutableblockposition, voxelshapecollision);
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index b5389cc46bf7afffe59522149e3d5567ad455ef3..90a491491272cfc6787e7ac778c23def0cf4b33a 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -677,6 +677,7 @@ public abstract class PlayerList {
         entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
         // CraftBukkit end
 
+        worldserver.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
         while (avoidSuffocation && !worldserver.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
             entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
         }
diff --git a/src/main/java/net/minecraft/server/VoxelShapes.java b/src/main/java/net/minecraft/server/VoxelShapes.java
index 335142ec0dfad822cfe40877e99dec0f307e1a58..e641d4b8d8b4ac5d539e6d9c0eaf345601e0b499 100644
--- a/src/main/java/net/minecraft/server/VoxelShapes.java
+++ b/src/main/java/net/minecraft/server/VoxelShapes.java
@@ -239,7 +239,8 @@ public final class VoxelShapes {
 
                             if (k2 < 3) {
                                 blockposition_mutableblockposition.a(enumaxiscycle1, i2, j2, l1);
-                                IBlockData iblockdata = iworldreader.getType(blockposition_mutableblockposition);
+                                IBlockData iblockdata = iworldreader.getTypeIfLoaded(blockposition_mutableblockposition); // Paper
+                                if (iblockdata == null) return 0.0D; // Paper
 
                                 if ((k2 != 1 || iblockdata.f()) && (k2 != 2 || iblockdata.getBlock() == Blocks.MOVING_PISTON)) {
                                     d0 = iblockdata.b((IBlockAccess) iworldreader, blockposition_mutableblockposition, voxelshapecollision).a(enumdirection_enumaxis2, axisalignedbb.d((double) (-blockposition_mutableblockposition.getX()), (double) (-blockposition_mutableblockposition.getY()), (double) (-blockposition_mutableblockposition.getZ())), d0);
