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 0be741820fc7da2aac4f4aad85c4238ef49a0f57..337976c5c1ead87c36daa4e741b06e5a195b8302 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -555,7 +555,7 @@ public class ServerGamePacketListenerImpl
                     return;
                 }
 
-                boolean flag = serverLevel.noCollision(rootVehicle, rootVehicle.getBoundingBox().deflate(0.0625));
+                final AABB oldBox = rootVehicle.getBoundingBox(); // Paper - copy from player movement packet
                 d3 = d - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above
                 d4 = d1 - this.vehicleLastGoodY; // Paper - diff on change, used for checking large move vectors above
                 d5 = d2 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above
@@ -565,6 +565,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; // Paper - Decompile fix: lvt reassignment lost
                 d3 = d - rootVehicle.getX();
                 d4 = d1 - rootVehicle.getY();
@@ -576,14 +577,22 @@ public class ServerGamePacketListenerImpl
                 d7 = d3 * d3 + d4 * d4 + d5 * d5;
                 boolean flag2 = false;
                 if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
-                    flag2 = true;
+                    flag2 = 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));
                 }
 
                 rootVehicle.absSnapTo(d, d1, d2, f, f1);
                 this.player.absSnapTo(d, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
-                boolean flag3 = serverLevel.noCollision(rootVehicle, rootVehicle.getBoundingBox().deflate(0.0625));
-                if (flag && (flag2 || !flag3)) {
+                // Paper start - optimise out extra getCubes
+                boolean teleportBack = flag2; // violating this is always a fail
+                if (!teleportBack) {
+                    // note: only call after setLocation, or else getBoundingBox is wrong
+                    final AABB newBox = rootVehicle.getBoundingBox();
+                    if (didCollide || !oldBox.equals(newBox)) {
+                        teleportBack = this.hasNewCollision(serverLevel, rootVehicle, oldBox, 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.player.absSnapTo(x, y, z, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
                     this.send(ClientboundMoveVehiclePacket.fromEntity(rootVehicle));
@@ -661,9 +670,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
@@ -1432,7 +1464,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
@@ -1471,6 +1503,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.
@@ -1503,7 +1536,17 @@ public class ServerGamePacketListenerImpl
                                 }
 
                                 // Paper start - Add fail move event
-                                boolean teleportBack = !this.player.noPhysics && !this.player.isSleeping() && (movedWrongly && serverLevel.noCollision(this.player, boundingBox) || this.isPlayerCollidingWithAnythingNew(serverLevel, boundingBox, d, d1, d2));
+                                // Paper start - optimise out extra getCubes
+                                boolean teleportBack = !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() && !teleportBack) {
+                                    final AABB newBox = this.player.getBoundingBox();
+                                    if (didCollide || !boundingBox.equals(newBox)) {
+                                        // note: only call after setLocation, or else getBoundingBox is wrong
+                                        teleportBack = this.hasNewCollision(serverLevel, this.player, boundingBox, newBox);
+                                    } // else: no collision at all detected, why do we care?
+                                }
+                                // Paper end - optimise out extra getCubes
                                 if (teleportBack) {
                                     io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK,
                                             toX, toY, toZ, toYaw, toPitch, false);
@@ -1640,7 +1683,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,
@@ -1659,6 +1702,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 isPlayerCollidingWithAnythingNew(LevelReader level, AABB box, double x, double y, double z) {
         AABB aabb = this.player.getBoundingBox().move(x - this.player.getX(), y - this.player.getY(), z - this.player.getZ());
         Iterable<VoxelShape> collisions = level.getCollisions(this.player, aabb.deflate(1.0E-5F));