From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf 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 522d6e59038db276238720d828e8949bbeba88d2..b93e6c14141f3ed3bc74c786eb56cf462510218a 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -613,7 +613,7 @@ 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 @@ -621,6 +621,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser boolean flag1 = entity.verticalCollisionBelow; 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(); @@ -634,16 +635,24 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser boolean flag2 = false; if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot - flag2 = true; + flag2 = true; // Paper - diff on change, this should be moved wrongly ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", new Object[]{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 flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D)); - 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 + 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)); @@ -729,7 +738,32 @@ 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.getBlockStateIfLoaded(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 @@ -1281,7 +1315,7 @@ 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()); } @@ -1375,7 +1409,7 @@ 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 @@ -1416,6 +1450,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser boolean flag1 = this.player.verticalCollisionBelow; 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.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move // Paper start - prevent position desync if (this.awaitingPositionFromClient != null) { @@ -1435,12 +1470,23 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser boolean flag2 = false; if (!this.player.isChangingDimension() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot - flag2 = true; + flag2 = 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() && (flag2 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb))) { + // Paper start - optimise out extra getCubes + // Original for reference: + // boolean teleportBack = flag2 && worldserver.getCubes(this.player, axisalignedbb) || (didCollide && this.a((IWorldReader) worldserver, axisalignedbb)); + boolean teleportBack = flag2; // 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.internalTeleport(d3, d4, d5, f, f1, Collections.emptySet(), false); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet. } else { // CraftBukkit start - fire PlayerMoveEvent @@ -1526,6 +1572,27 @@ 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 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) { Iterable iterable = world.getCollisions(this.player, this.player.getBoundingBox().deflate(9.999999747378752E-6D)); VoxelShape voxelshape = Shapes.create(box.deflate(9.999999747378752E-6D));