From 68e28c6750a1328bcecfe57631366e6d86600c11 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Tue, 26 Sep 2023 09:16:11 -0700 Subject: [PATCH] Fix VoxelShape#isFullBlock() for non-single AABB types The correct logic to implement NOT_SAME with Shapes#block() is to test whether any shape data exists outside of [0.0, 1.0] and to test whether the shape is completely filled from 0.0 to 1.0 on all axis. This can be implemented by checking whether the bounds represent the full block and whether everything within the bounds is filled. --- patches/server/Collision-optimisations.patch | 47 +++++++++++++++++--- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/patches/server/Collision-optimisations.patch b/patches/server/Collision-optimisations.patch index 48023fe3a2..dbb9cba805 100644 --- a/patches/server/Collision-optimisations.patch +++ b/patches/server/Collision-optimisations.patch @@ -4220,7 +4220,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + private boolean computeFullBlock() { -+ final Boolean ret; ++ Boolean ret; + if (this.isEmpty) { + ret = Boolean.FALSE; + } else if ((VoxelShape)(Object)this == Shapes.block()) { @@ -4228,8 +4228,45 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } else { + final AABB singleAABB = this.singleAABBRepresentation; + if (singleAABB == null) { -+ // note: Shapes.join(BLOCK, this, NOT_SAME) cannot be empty when voxelSize > 2 -+ ret = Boolean.FALSE; ++ final io.papermc.paper.util.collisions.CachedShapeData shapeData = this.cachedShapeData; ++ final int sMinX = shapeData.minFullX(); ++ final int sMinY = shapeData.minFullY(); ++ final int sMinZ = shapeData.minFullZ(); ++ ++ final int sMaxX = shapeData.maxFullX(); ++ final int sMaxY = shapeData.maxFullY(); ++ final int sMaxZ = shapeData.maxFullZ(); ++ ++ if (Math.abs(this.rootCoordinatesX[sMinX] + this.offsetX) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && ++ Math.abs(this.rootCoordinatesY[sMinY] + this.offsetY) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && ++ Math.abs(this.rootCoordinatesZ[sMinZ] + this.offsetZ) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && ++ ++ Math.abs(1.0 - (this.rootCoordinatesX[sMaxX] + this.offsetX)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && ++ Math.abs(1.0 - (this.rootCoordinatesY[sMaxY] + this.offsetY)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && ++ Math.abs(1.0 - (this.rootCoordinatesZ[sMaxZ] + this.offsetZ)) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON) { ++ ++ // index = z + y*sizeZ + x*(sizeZ*sizeY) ++ ++ final int sizeY = shapeData.sizeY(); ++ final int sizeZ = shapeData.sizeZ(); ++ ++ final long[] bitset = shapeData.voxelSet(); ++ ++ ret = Boolean.TRUE; ++ ++ check_full: ++ for (int x = sMinX; x < sMaxX; ++x) { ++ for (int y = sMinY; y < sMaxY; ++y) { ++ final int baseIndex = y*sizeZ + x*(sizeZ*sizeY); ++ if (!io.papermc.paper.util.collisions.FlatBitsetUtil.isRangeSet(bitset, baseIndex + sMinZ, baseIndex + sMaxZ)) { ++ ret = Boolean.FALSE; ++ break check_full; ++ } ++ } ++ } ++ } else { ++ ret = Boolean.FALSE; ++ } + } else { + ret = Boolean.valueOf( + Math.abs(singleAABB.minX) <= io.papermc.paper.util.CollisionUtil.COLLISION_EPSILON && @@ -4575,11 +4612,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); } + return clip(singleAABB, start, end, pos); -+ } + } + + if (io.papermc.paper.util.CollisionUtil.strictlyContains(this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) { + return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), pos, true); - } ++ } + + return AABB.clip(this.toAabbs(), start, end, pos); + // Paper end - optimise collisions