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/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ public abstract class PlayerList {
         entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
         // CraftBukkit end
 
+        worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
         while (avoidSuffocation && !worldserver1.noCollision(entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
             entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
         }
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n
     // Paper end
 
     public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
+    public boolean collisionLoadChunks = false; // Paper
     private CraftEntity bukkitEntity;
 
     public net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -0,0 +0,0 @@ public interface CollisionGetter extends BlockGetter {
     }
 
     default boolean noCollision(@Nullable Entity entity, AABB box, Predicate<Entity> filter) {
+        try { if (entity != null) entity.collisionLoadChunks = true; // Paper
         return this.getCollisions(entity, box, filter).allMatch(VoxelShape::isEmpty);
+        } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
     }
 
     Stream<VoxelShape> getEntityCollisions(@Nullable Entity entity, AABB box, Predicate<Entity> predicate);
diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
+++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java
@@ -0,0 +0,0 @@ import net.minecraft.world.phys.shapes.VoxelShape;
 
 public class CollisionSpliterator extends AbstractSpliterator<VoxelShape> {
     @Nullable
-    private final Entity source;
+    private final Entity source; final Entity getEntity() { return this.source; } // Paper - OBFHELPER
     private final AABB box;
     private final CollisionContext context;
     private final Cursor3D cursor;
-    private final BlockPos.MutableBlockPos pos;
+    private final BlockPos.MutableBlockPos pos; final BlockPos.MutableBlockPos getMutablePos() { return this.pos; } // Paper - OBFHELPER
     private final VoxelShape entityShape;
-    private final CollisionGetter collisionGetter;
+    private final CollisionGetter collisionGetter; final CollisionGetter getCollisionAccess() { return this.collisionGetter; } // Paper - OBFHELPER
     private boolean needsBorderCheck;
     private final BiPredicate<BlockState, BlockPos> predicate;
 
@@ -0,0 +0,0 @@ public class CollisionSpliterator extends AbstractSpliterator<VoxelShape> {
     boolean collisionCheck(Consumer<? super VoxelShape> action) {
         while(true) {
             if (this.cursor.advance()) {
-                int i = this.cursor.nextX();
-                int j = this.cursor.nextY();
-                int k = this.cursor.nextZ();
+                int i = this.cursor.nextX(); final int x = i;
+                int j = this.cursor.nextY(); final int y = j;
+                int k = this.cursor.nextZ(); final int z = k;
                 int l = this.cursor.getNextType();
                 if (l == 3) {
                     continue;
                 }
 
-                BlockGetter blockGetter = this.getChunk(i, k);
-                if (blockGetter == null) {
+                // Paper start - ensure we don't load chunks
+                Entity entity = this.getEntity();
+                BlockPos.MutableBlockPos blockposition_mutableblockposition = this.getMutablePos();
+                boolean far = entity != null && net.minecraft.server.MCUtil.distanceSq(entity.getX(), y, entity.getZ(), x, y, z) > 14;
+                blockposition_mutableblockposition.setValues(x, y, z);
+
+                boolean isRegionLimited = this.getCollisionAccess() instanceof net.minecraft.server.level.WorldGenRegion;
+                BlockState blockState = isRegionLimited ? Blocks.VOID_AIR.defaultBlockState() : ((!far && entity instanceof net.minecraft.server.level.ServerPlayer) || (entity != null && entity.collisionLoadChunks)
+                    ? this.getCollisionAccess().getBlockState(blockposition_mutableblockposition)
+                    : this.getCollisionAccess().getTypeIfLoaded(blockposition_mutableblockposition)
+                );
+
+                if (blockState == null) {
+                    if (!(entity instanceof net.minecraft.server.level.ServerPlayer) || entity.level.paperConfig.preventMovingIntoUnloadedChunks) {
+                        VoxelShape voxelshape3 = Shapes.create(far ? entity.getBoundingBox() : new AABB(new BlockPos(x, y, z)));
+                        action.accept(voxelshape3);
+                        return true;
+                    }
                     continue;
                 }
+                // Paper - moved up
+                // Paper end
 
-                this.pos.set(i, j, k);
-                BlockState blockState = blockGetter.getBlockState(this.pos);
                 if (!this.predicate.test(blockState, this.pos) || l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) {
                     continue;
                 }
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
@@ -0,0 +0,0 @@ public final class Shapes {
 
                             if (s < 3) {
                                 mutableBlockPos.set(axisCycle, q, r, p);
-                                BlockState blockState = world.getBlockState(mutableBlockPos);
+                                BlockState blockState = world.getTypeIfLoaded(mutableBlockPos); // Paper
+                                if (blockState == null) return 0.0D; // Paper
                                 if ((s != 1 || blockState.hasLargeCollisionShape()) && (s != 2 || blockState.is(Blocks.MOVING_PISTON))) {
                                     initial = blockState.getCollisionShape(world, mutableBlockPos, context).collide(axis3, box.move((double)(-mutableBlockPos.getX()), (double)(-mutableBlockPos.getY()), (double)(-mutableBlockPos.getZ())), initial);
                                     if (Math.abs(initial) < 1.0E-7D) {
