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/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java index ff2f83dc1cbf10baccd07e65c894dff1d2f9a3cf..63d01c3753b865532a222b5b7408c8857c064d55 100644 --- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -594,6 +594,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(); @@ -605,12 +606,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(); @@ -689,9 +699,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 @@ -1467,7 +1500,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 @@ -1506,6 +1539,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. @@ -1538,7 +1572,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); @@ -1674,7 +1718,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, @@ -1693,6 +1737,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 collisionsBB = new java.util.ArrayList<>(); + final List 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 preMoveCollisions = levelReader.getPreMoveCollisions(entity, aabb1.deflate(1.0E-5F), aabb.getBottomCenter());