From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 2 Jul 2020 12:02:43 -0700
Subject: [PATCH] Optimise collision checking in player move packet handling

Move collision logic to just the hasNewCollision call instead of getCubes + hasNewCollision

diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 08aba415735733f5968fd032ab7ca249cdcf6cde..ee4397711625344622c81424afd11fd6d967efba 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -606,6 +606,7 @@ public class ServerGamePacketListenerImpl
                 }
 
                 rootVehicle.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
+                final boolean didCollide = toX != rootVehicle.getX() || toY != rootVehicle.getY() || toZ != rootVehicle.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be...
                 double verticalDelta = d4;
                 d3 = d - rootVehicle.getX();
                 d4 = d1 - rootVehicle.getY();
@@ -617,12 +618,21 @@ public class ServerGamePacketListenerImpl
                 d7 = d3 * d3 + d4 * d4 + d5 * d5;
                 boolean flag1 = false;
                 if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
-                    flag1 = true;
+                    flag1 = true; // Paper - diff on change, this should be moved wrongly
                     LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7));
                 }
 
-                if (flag1 && serverLevel.noCollision(rootVehicle, boundingBox)
-                    || this.isEntityCollidingWithAnythingNew(serverLevel, rootVehicle, boundingBox, d, d1, d2)) {
+                // Paper start - optimise out extra getCubes
+                boolean teleportBack = flag1;
+                if (!teleportBack) {
+                    // note: only call after setLocation, or else getBoundingBox is wrong
+                    final AABB newBox = rootVehicle.getBoundingBox();
+                    if (didCollide || !boundingBox.equals(newBox)) {
+                        teleportBack = this.hasNewCollision(serverLevel, rootVehicle, boundingBox, newBox);
+                    } // else: no collision at all detected, why do we care?
+                }
+                if (teleportBack) {
+                // Paper end - optimise out extra getCubes
                     rootVehicle.absSnapTo(x, y, z, f, f1);
                     this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
                     rootVehicle.removeLatestMovementRecording();
@@ -701,9 +711,32 @@ public class ServerGamePacketListenerImpl
     }
 
     private boolean noBlocksAround(Entity entity) {
-        return entity.level()
-            .getBlockStates(entity.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0))
-            .allMatch(BlockBehaviour.BlockStateBase::isAir);
+        // Paper start - stop using streams, this is already a known fixed problem in Entity#move
+        final AABB box = entity.getBoundingBox().inflate(0.0625).expandTowards(0.0, -0.55, 0.0);
+        final int minX = Mth.floor(box.minX);
+        final int minY = Mth.floor(box.minY);
+        final int minZ = Mth.floor(box.minZ);
+        final int maxX = Mth.floor(box.maxX);
+        final int maxY = Mth.floor(box.maxY);
+        final int maxZ = Mth.floor(box.maxZ);
+
+        final Level level = entity.level();
+        final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
+
+        for (int y = minY; y <= maxY; ++y) {
+            for (int z = minZ; z <= maxZ; ++z) {
+                for (int x = minX; x <= maxX; ++x) {
+                    pos.set(x, y, z);
+                    final BlockState blockState = level.getBlockStateIfLoaded(pos);
+                    if (blockState != null && !blockState.isAir()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+        // Paper end - stop using streams, this is already a known fixed problem in Entity#move
     }
 
     @Override
@@ -1479,7 +1512,7 @@ public class ServerGamePacketListenerImpl
                                     }
                                 }
 
-                                AABB boundingBox = this.player.getBoundingBox();
+                                AABB boundingBox = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB
                                 d3 = d - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
                                 d4 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
                                 d5 = d2 - this.lastGoodZ; // Paper - diff on change, used for checking large move vectors above
@@ -1518,6 +1551,7 @@ public class ServerGamePacketListenerImpl
                                 boolean flag1 = this.player.verticalCollisionBelow;
                                 this.player.move(MoverType.PLAYER, new Vec3(d3, d4, d5));
                                 this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
+                                final boolean didCollide = toX != this.player.getX() || toY != this.player.getY() || toZ != this.player.getZ(); // Paper - needed here as the difference in Y can be reset - also note: this is only a guess at whether collisions took place, floating point errors can make this true when it shouldn't be...
                                 // Paper start - prevent position desync
                                 if (this.awaitingPositionFromClient != null) {
                                     return; // ... thanks Mojang for letting move calls teleport across dimensions.
@@ -1550,7 +1584,17 @@ public class ServerGamePacketListenerImpl
                                 }
 
                                 // Paper start - Add fail move event
-                                boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || (!movedWrongly || !serverLevel.noCollision(this.player, boundingBox)) && !this.isEntityCollidingWithAnythingNew(serverLevel, this.player, boundingBox, d, d1, d2);
+                                // Paper start - optimise out extra getCubes
+                                boolean allowMovement = this.player.noPhysics || this.player.isSleeping() || !movedWrongly;
+                                this.player.absSnapTo(d, d1, d2, f, f1); // prevent desync by tping to the set position, dropped for unknown reasons by mojang
+                                if (!this.player.noPhysics && !this.player.isSleeping() && allowMovement) {
+                                    final AABB newBox = this.player.getBoundingBox();
+                                    if (didCollide || !boundingBox.equals(newBox)) {
+                                        // note: only call after setLocation, or else getBoundingBox is wrong
+                                        allowMovement = !this.hasNewCollision(serverLevel, this.player, boundingBox, newBox);
+                                    } // else: no collision at all detected, why do we care?
+                                }
+                                // Paper end - optimise out extra getCubes
                                 if (!allowMovement) {
                                     io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
                                             toX, toY, toZ, toYaw, toPitch, false);
@@ -1686,7 +1730,7 @@ public class ServerGamePacketListenerImpl
 
     private boolean updateAwaitingTeleport() {
         if (this.awaitingPositionFromClient != null) {
-            if (this.tickCount - this.awaitingTeleportTime > 20) {
+            if (false && this.tickCount - this.awaitingTeleportTime > 20) { // Paper - this will greatly screw with clients with > 1000ms RTT
                 this.awaitingTeleportTime = this.tickCount;
                 this.teleport(
                     this.awaitingPositionFromClient.x,
@@ -1705,6 +1749,33 @@ public class ServerGamePacketListenerImpl
         }
     }
 
+    // Paper start - optimise out extra getCubes
+    private boolean hasNewCollision(final ServerLevel level, final Entity entity, final AABB oldBox, final AABB newBox) {
+        final List<AABB> collisionsBB = new java.util.ArrayList<>();
+        final List<VoxelShape> collisionsVoxel = new java.util.ArrayList<>();
+        ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions(
+            level, entity, newBox, collisionsVoxel, collisionsBB,
+            ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS | ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER,
+            null, null
+        );
+
+        for (int i = 0, len = collisionsBB.size(); i < len; ++i) {
+            final AABB box = collisionsBB.get(i);
+            if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersect(box, oldBox)) {
+                return true;
+            }
+        }
+
+        for (int i = 0, len = collisionsVoxel.size(); i < len; ++i) {
+            final VoxelShape voxel = collisionsVoxel.get(i);
+            if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(voxel, oldBox)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+    // Paper end - optimise out extra getCubes
     private boolean isEntityCollidingWithAnythingNew(LevelReader levelReader, Entity entity, AABB aabb, double d, double d1, double d2) {
         AABB aabb1 = entity.getBoundingBox().move(d - entity.getX(), d1 - entity.getY(), d2 - entity.getZ());
         Iterable<VoxelShape> preMoveCollisions = levelReader.getPreMoveCollisions(entity, aabb1.deflate(1.0E-5F), aabb.getBottomCenter());
