168 lines
		
	
	
	
		
			11 KiB
			
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
	
		
			11 KiB
			
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
						|
From: Spottedleaf <spottedleaf@spottedleaf.dev>
 | 
						|
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/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
						|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 | 
						|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
						|
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                     return;
 | 
						|
                 }
 | 
						|
 
 | 
						|
-                boolean flag = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
 | 
						|
+                AABB oldBox = entity.getBoundingBox(); // Paper - copy from player movement packet
 | 
						|
 
 | 
						|
                 d6 = d3 - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above
 | 
						|
                 d7 = d4 - this.vehicleLastGoodY - 1.0E-6D; // Paper - diff on change, used for checking large move vectors above
 | 
						|
                 d8 = d5 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above
 | 
						|
                 entity.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
 | 
						|
+                boolean didCollide = toX != entity.getX() || toY != entity.getY() || toZ != entity.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 d11 = d7;
 | 
						|
 
 | 
						|
                 d6 = d3 - entity.getX();
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                 boolean flag1 = false;
 | 
						|
 
 | 
						|
                 if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
 | 
						|
-                    flag1 = true;
 | 
						|
+                    flag1 = true; // Paper - diff on change, this should be moved wrongly
 | 
						|
                     ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10));
 | 
						|
                 }
 | 
						|
                 Location curPos = this.getCraftPlayer().getLocation(); // Spigot
 | 
						|
 
 | 
						|
                 entity.absMoveTo(d3, d4, d5, f, f1);
 | 
						|
                 this.player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
 | 
						|
-                boolean flag2 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
 | 
						|
-
 | 
						|
-                if (flag && (flag1 || !flag2)) {
 | 
						|
+                // Paper start - optimise out extra getCubes
 | 
						|
+                boolean teleportBack = flag1; // violating this is always a fail
 | 
						|
+                if (!teleportBack) {
 | 
						|
+                    // note: only call after setLocation, or else getBoundingBox is wrong
 | 
						|
+                    AABB newBox = entity.getBoundingBox();
 | 
						|
+                    if (didCollide || !oldBox.equals(newBox)) {
 | 
						|
+                        teleportBack = this.hasNewCollision(worldserver, entity, oldBox, newBox);
 | 
						|
+                    } // else: no collision at all detected, why do we care?
 | 
						|
+                }
 | 
						|
+                if (teleportBack) { // Paper end - optimise out extra getCubes
 | 
						|
                     entity.absMoveTo(d0, d1, d2, f, f1);
 | 
						|
                     this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
 | 
						|
                     this.connection.send(new ClientboundMoveVehiclePacket(entity));
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
     }
 | 
						|
 
 | 
						|
     private boolean noBlocksAround(Entity entity) {
 | 
						|
-        return entity.level.getBlockStates(entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D)).allMatch(BlockBehaviour.BlockStateBase::isAir);
 | 
						|
+        // Paper start - stop using streams, this is already a known fixed problem in Entity#move
 | 
						|
+        AABB box = entity.getBoundingBox().inflate(0.0625D).expandTowards(0.0D, -0.55D, 0.0D);
 | 
						|
+        int minX = Mth.floor(box.minX);
 | 
						|
+        int minY = Mth.floor(box.minY);
 | 
						|
+        int minZ = Mth.floor(box.minZ);
 | 
						|
+        int maxX = Mth.floor(box.maxX);
 | 
						|
+        int maxY = Mth.floor(box.maxY);
 | 
						|
+        int maxZ = Mth.floor(box.maxZ);
 | 
						|
+
 | 
						|
+        Level world = entity.level;
 | 
						|
+        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);
 | 
						|
+                    BlockState type = world.getTypeIfLoaded(pos);
 | 
						|
+                    if (type != null && !type.isAir()) {
 | 
						|
+                        return false;
 | 
						|
+                    }
 | 
						|
+                }
 | 
						|
+            }
 | 
						|
+        }
 | 
						|
+
 | 
						|
+        return true;
 | 
						|
+        // Paper end - stop using streams, this is already a known fixed problem in Entity#move
 | 
						|
     }
 | 
						|
 
 | 
						|
     @Override
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                 }
 | 
						|
 
 | 
						|
                 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, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
 | 
						|
                     }
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                                 }
 | 
						|
                             }
 | 
						|
 
 | 
						|
-                            AABB axisalignedbb = this.player.getBoundingBox();
 | 
						|
+                            AABB axisalignedbb = this.player.getBoundingBox(); // Paper - diff on change, should be old AABB
 | 
						|
 
 | 
						|
                             d7 = d0 - this.lastGoodX; // Paper - diff on change, used for checking large move vectors above
 | 
						|
                             d8 = d1 - this.lastGoodY; // Paper - diff on change, used for checking large move vectors above
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                             }
 | 
						|
 
 | 
						|
                             this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9));
 | 
						|
+                            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...
 | 
						|
                             this.player.setOnGround(packet.isOnGround()); // CraftBukkit - SPIGOT-5810, SPIGOT-5835: reset by this.player.move
 | 
						|
                             // Paper start - prevent position desync
 | 
						|
                             if (this.awaitingPositionFromClient != null) {
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
                             boolean flag1 = false;
 | 
						|
 
 | 
						|
                             if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot
 | 
						|
-                                flag1 = true;
 | 
						|
+                                flag1 = true; // Paper - diff on change, this should be moved wrongly
 | 
						|
                                 ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
 | 
						|
                             }
 | 
						|
 
 | 
						|
                             this.player.absMoveTo(d0, d1, d2, f, f1);
 | 
						|
-                            if (!this.player.noPhysics && !this.player.isSleeping() && (flag1 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew((LevelReader) worldserver, axisalignedbb))) {
 | 
						|
+                            // Paper start - optimise out extra getCubes
 | 
						|
+                            // Original for reference:
 | 
						|
+                            // boolean teleportBack = flag1 && worldserver.getCubes(this.player, axisalignedbb) || (didCollide && this.a((IWorldReader) worldserver, axisalignedbb));
 | 
						|
+                            boolean teleportBack = flag1; // violating this is always a fail
 | 
						|
+                            if (!this.player.noPhysics && !this.player.isSleeping() && !teleportBack) {
 | 
						|
+                                AABB newBox = this.player.getBoundingBox();
 | 
						|
+                                if (didCollide || !axisalignedbb.equals(newBox)) {
 | 
						|
+                                    // note: only call after setLocation, or else getBoundingBox is wrong
 | 
						|
+                                    teleportBack = this.hasNewCollision(worldserver, this.player, axisalignedbb, newBox);
 | 
						|
+                                } // else: no collision at all detected, why do we care?
 | 
						|
+                            }
 | 
						|
+                            if (!this.player.noPhysics && !this.player.isSleeping() && teleportBack) { // Paper end - optimise out extra getCubes
 | 
						|
                                 this.teleport(d3, d4, d5, f, f1);
 | 
						|
                             } else {
 | 
						|
                                 // CraftBukkit start - fire PlayerMoveEvent
 | 
						|
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
 | 
						|
         }
 | 
						|
     }
 | 
						|
 
 | 
						|
+    // Paper start - optimise out extra getCubes
 | 
						|
+    private boolean hasNewCollision(final ServerLevel world, final Entity entity, final AABB oldBox, final AABB newBox) {
 | 
						|
+        final List<AABB> collisions = io.papermc.paper.util.CachedLists.getTempCollisionList();
 | 
						|
+        try {
 | 
						|
+            io.papermc.paper.util.CollisionUtil.getCollisions(world, entity, newBox, collisions, false, true,
 | 
						|
+                true, false, null, null);
 | 
						|
+
 | 
						|
+            for (int i = 0, len = collisions.size(); i < len; ++i) {
 | 
						|
+                final AABB box = collisions.get(i);
 | 
						|
+                if (!io.papermc.paper.util.CollisionUtil.voxelShapeIntersect(box, oldBox)) {
 | 
						|
+                    return true;
 | 
						|
+                }
 | 
						|
+            }
 | 
						|
+
 | 
						|
+            return false;
 | 
						|
+        } finally {
 | 
						|
+            io.papermc.paper.util.CachedLists.returnTempCollisionList(collisions);
 | 
						|
+        }
 | 
						|
+    }
 | 
						|
+    // Paper end - optimise out extra getCubes
 | 
						|
+
 | 
						|
     private boolean isPlayerCollidingWithAnythingNew(LevelReader world, AABB box) {
 | 
						|
         Stream<VoxelShape> stream = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D), (entity) -> {
 | 
						|
             return true;
 |