From fcca15ff0bee51e105dcf6d9f79c4e5ea2673522 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sun, 5 Dec 2021 00:38:00 -0800 Subject: [PATCH] Update Highly optimise single and multi-AABB VoxelShapes and collisions --- ...ingle-and-multi-AABB-VoxelShapes-an.patch} | 291 +++++++++--------- 1 file changed, 139 insertions(+), 152 deletions(-) rename patches/{removed/1.18/0764-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch => server/0825-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch} (89%) diff --git a/patches/removed/1.18/0764-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch b/patches/server/0825-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch similarity index 89% rename from patches/removed/1.18/0764-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch rename to patches/server/0825-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch index fcfb42070..87fcc1fbb 100644 --- a/patches/removed/1.18/0764-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch +++ b/patches/server/0825-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch @@ -70,10 +70,10 @@ index be668387f65a633c6ac497fca632a4767a1bf3a2..e08f4e39db4ee3fed62e37364d17dcc5 } diff --git a/src/main/java/io/papermc/paper/util/CollisionUtil.java b/src/main/java/io/papermc/paper/util/CollisionUtil.java new file mode 100644 -index 0000000000000000000000000000000000000000..98ca1199a823cdf55b913396ce0a24554e85f116 +index 0000000000000000000000000000000000000000..58629451977c89db2fa895bde946135784a0d8bc --- /dev/null +++ b/src/main/java/io/papermc/paper/util/CollisionUtil.java -@@ -0,0 +1,645 @@ +@@ -0,0 +1,639 @@ +package io.papermc.paper.util; + +import io.papermc.paper.voxel.AABBVoxelShape; @@ -564,7 +564,7 @@ index 0000000000000000000000000000000000000000..98ca1199a823cdf55b913396ce0a2455 + + for (int currY = minYIterate; currY <= maxYIterate; ++currY) { + LevelChunkSection section = sections[(currY >> 4) - minSection]; -+ if (section == null || section.isEmpty()) { ++ if (section.hasOnlyAir()) { + // empty + // skip to next section + currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one @@ -678,15 +678,14 @@ index 0000000000000000000000000000000000000000..98ca1199a823cdf55b913396ce0a2455 + public static final class LazyEntityCollisionContext extends EntityCollisionContext { + + private CollisionContext delegate; -+ private final Entity entity; + + public LazyEntityCollisionContext(final Entity entity) { -+ super(false, 0.0, null, null, null, Optional.ofNullable(entity)); -+ this.entity = entity; ++ super(false, 0.0, null, null, entity); + } + + public CollisionContext getDelegate() { -+ return this.delegate == null ? this.delegate = (this.entity == null ? CollisionContext.empty() : CollisionContext.of(this.entity)) : this.delegate; ++ final Entity entity = this.getEntity(); ++ return this.delegate == null ? this.delegate = (entity == null ? CollisionContext.empty() : CollisionContext.of(entity)) : this.delegate; + } + + @Override @@ -700,11 +699,6 @@ index 0000000000000000000000000000000000000000..98ca1199a823cdf55b913396ce0a2455 + } + + @Override -+ public boolean hasItemOnFeet(final Item item) { -+ return this.getDelegate().hasItemOnFeet(item); -+ } -+ -+ @Override + public boolean isHoldingItem(final Item item) { + return this.getDelegate().isHoldingItem(item); + } @@ -926,45 +920,45 @@ index 0000000000000000000000000000000000000000..d67a40e7be030142443680c89e1763fc + } +} diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index e40587b9c13d9170ef5dd0811e96b02ecaccebb2..52ffc39715def70a8ad3a99356be19bea5ef892b 100644 +index b6f9c99d580d985f2b84efaa655d9ae40073e72b..e0beb35868a564dbcc0369d0a56b94642a23436a 100644 --- a/src/main/java/net/minecraft/server/level/ServerPlayer.java +++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -419,7 +419,7 @@ public class ServerPlayer extends Player { +@@ -415,7 +415,7 @@ public class ServerPlayer extends Player { if (blockposition1 != null) { this.moveTo(blockposition1, 0.0F, 0.0F); -- if (world.noCollision(this)) { -+ if (world.noCollision(this, this.getBoundingBox(), null, true)) { // Paper - make sure this loads chunks, we default to NOT loading now +- if (world.noCollision((Entity) this)) { ++ if (world.noCollision(this, this.getBoundingBox(), true)) { // Paper - make sure this loads chunks, we default to NOT loading now break; } } -@@ -427,7 +427,7 @@ public class ServerPlayer extends Player { +@@ -423,7 +423,7 @@ public class ServerPlayer extends Player { } else { this.moveTo(blockposition, 0.0F, 0.0F); -- while (!world.noCollision(this) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { -+ while (!world.noCollision(this, this.getBoundingBox(), null, true) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { // Paper - make sure this loads chunks, we default to NOT loading now +- while (!world.noCollision((Entity) this) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { ++ while (!world.noCollision(this, this.getBoundingBox(), true) && this.getY() < (double) (world.getMaxBuildHeight() - 1)) { // Paper - make sure this loads chunks, we default to NOT loading now this.setPos(this.getX(), this.getY() + 1.0D, this.getZ()); } } diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 450dd486269450be88c9a9a4d895184199e655aa..9b6770a41d272eea4b0a1d2076c936af3eaf5c2c 100644 +index 25b787d1b22e495fb6756e4ee909776ed8699492..042be2cf60a9d01698808d84f2e537a5eb952079 100644 --- a/src/main/java/net/minecraft/server/players/PlayerList.java +++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -935,7 +935,7 @@ public abstract class PlayerList { +@@ -932,7 +932,7 @@ public abstract class PlayerList { + // CraftBukkit end worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper - entityplayer1.forceCheckHighPriority(); // Player - Chunk priority - while (avoidSuffocation && !worldserver1.noCollision(entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) { -+ while (avoidSuffocation && !worldserver1.noCollision(entityplayer1, entityplayer1.getBoundingBox(), null, true) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) { // Paper - make sure this loads chunks, we default to NOT loading now ++ while (avoidSuffocation && !worldserver1.noCollision(entityplayer1, entityplayer1.getBoundingBox(), true) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) { // Paper - make sure this loads chunks, we default to NOT loading now entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ()); } // CraftBukkit start diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed35c87b3c 100644 +index 516015eb48900abaf0e2f47de4ffd10e9cf4d9a7..1dd646bb8d1c153a3d034de1c208b3bacebd067f 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1025,9 +1025,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n +@@ -1076,9 +1076,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i float f2 = this.getBlockSpeedFactor(); this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2)); @@ -1012,46 +1006,38 @@ index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed if (this.remainingFireTicks <= 0) { this.setRemainingFireTicks(-this.getFireImmuneTicks()); } -@@ -1149,39 +1184,79 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n - return offsetFactor; +@@ -1212,32 +1247,78 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i } -- private Vec3 collide(Vec3 movement) { + private Vec3 collide(Vec3 movement) { - AABB axisalignedbb = this.getBoundingBox(); -- CollisionContext voxelshapecollision = CollisionContext.of(this); -- VoxelShape voxelshape = this.level.getWorldBorder().getCollisionShape(); -- Stream stream = !this.level.getWorldBorder().isWithinBounds(axisalignedbb) ? Stream.empty() : Stream.of(voxelshape); // Paper -- Stream stream1 = this.level.getEntityCollisions(this, axisalignedbb.expandTowards(movement), (entity) -> { -- return true; -- }); -- RewindableStream streamaccumulator = new RewindableStream<>(Stream.concat(stream1, stream)); -- Vec3 vec3d1 = movement.lengthSqr() == 0.0D ? movement : Entity.collideBoundingBoxHeuristically(this, movement, axisalignedbb, this.level, voxelshapecollision, streamaccumulator); +- List list = this.level.getEntityCollisions(this, axisalignedbb.expandTowards(movement)); +- Vec3 vec3d1 = movement.lengthSqr() == 0.0D ? movement : Entity.collideBoundingBox(this, movement, axisalignedbb, this.level, list); - boolean flag = movement.x != vec3d1.x; - boolean flag1 = movement.y != vec3d1.y; - boolean flag2 = movement.z != vec3d1.z; - boolean flag3 = this.onGround || flag1 && movement.y < 0.0D; - - if (this.maxUpStep > 0.0F && flag3 && (flag || flag2)) { -- Vec3 vec3d2 = Entity.collideBoundingBoxHeuristically(this, new Vec3(movement.x, (double) this.maxUpStep, movement.z), axisalignedbb, this.level, voxelshapecollision, streamaccumulator); -- Vec3 vec3d3 = Entity.collideBoundingBoxHeuristically(this, new Vec3(0.0D, (double) this.maxUpStep, 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level, voxelshapecollision, streamaccumulator); +- Vec3 vec3d2 = Entity.collideBoundingBox(this, new Vec3(movement.x, (double) this.maxUpStep, movement.z), axisalignedbb, this.level, list); +- Vec3 vec3d3 = Entity.collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep, 0.0D), axisalignedbb.expandTowards(movement.x, 0.0D, movement.z), this.level, list); - - if (vec3d3.y < (double) this.maxUpStep) { -- Vec3 vec3d4 = Entity.collideBoundingBoxHeuristically(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level, voxelshapecollision, streamaccumulator).add(vec3d3); +- Vec3 vec3d4 = Entity.collideBoundingBox(this, new Vec3(movement.x, 0.0D, movement.z), axisalignedbb.move(vec3d3), this.level, list).add(vec3d3); - - if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { - vec3d2 = vec3d4; -+ private Vec3 collide(Vec3 moveVector) { + // Paper start - optimise collisions + // This is a copy of vanilla's except that it uses strictly AABB math -+ if (moveVector.x == 0.0 && moveVector.y == 0.0 && moveVector.z == 0.0) { -+ return moveVector; ++ if (movement.x == 0.0 && movement.y == 0.0 && movement.z == 0.0) { ++ return movement; + } + + final Level world = this.level; + final AABB currBoundingBox = this.getBoundingBox(); + + if (io.papermc.paper.util.CollisionUtil.isEmpty(currBoundingBox)) { -+ return moveVector; ++ return movement; + } + + final List potentialCollisions = io.papermc.paper.util.CachedLists.getTempCollisionList(); @@ -1059,27 +1045,27 @@ index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed + final double stepHeight = (double)this.maxUpStep; + final AABB collisionBox; + -+ if (moveVector.x == 0.0 && moveVector.z == 0.0 && moveVector.y != 0.0) { -+ if (moveVector.y > 0.0) { -+ collisionBox = io.papermc.paper.util.CollisionUtil.cutUpwards(currBoundingBox, moveVector.y); ++ if (movement.x == 0.0 && movement.z == 0.0 && movement.y != 0.0) { ++ if (movement.y > 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutUpwards(currBoundingBox, movement.y); + } else { -+ collisionBox = io.papermc.paper.util.CollisionUtil.cutDownwards(currBoundingBox, moveVector.y); ++ collisionBox = io.papermc.paper.util.CollisionUtil.cutDownwards(currBoundingBox, movement.y); + } + } else { -+ if (stepHeight > 0.0 && (this.onGround || (moveVector.y < 0.0)) && (moveVector.x != 0.0 || moveVector.z != 0.0)) { ++ if (stepHeight > 0.0 && (this.onGround || (movement.y < 0.0)) && (movement.x != 0.0 || movement.z != 0.0)) { + // don't bother getting the collisions if we don't need them. -+ if (moveVector.y <= 0.0) { -+ collisionBox = io.papermc.paper.util.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(moveVector.x, moveVector.y, moveVector.z), stepHeight); ++ if (movement.y <= 0.0) { ++ collisionBox = io.papermc.paper.util.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(movement.x, movement.y, movement.z), stepHeight); + } else { -+ collisionBox = currBoundingBox.expandTowards(moveVector.x, Math.max(stepHeight, moveVector.y), moveVector.z); ++ collisionBox = currBoundingBox.expandTowards(movement.x, Math.max(stepHeight, movement.y), movement.z); + } + } else { -+ collisionBox = currBoundingBox.expandTowards(moveVector.x, moveVector.y, moveVector.z); ++ collisionBox = currBoundingBox.expandTowards(movement.x, movement.y, movement.z); } } - if (vec3d2.horizontalDistanceSqr() > vec3d1.horizontalDistanceSqr()) { -- return vec3d2.add(Entity.collideBoundingBoxHeuristically(this, new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), axisalignedbb.move(vec3d2), this.level, voxelshapecollision, streamaccumulator)); +- return vec3d2.add(Entity.collideBoundingBox(this, new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), axisalignedbb.move(vec3d2), this.level, list)); + io.papermc.paper.util.CollisionUtil.getCollisions(world, this, collisionBox, potentialCollisions, false, true, + false, false, null, null); + @@ -1089,16 +1075,16 @@ index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed - } - return vec3d1; -+ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(moveVector, currBoundingBox, potentialCollisions); ++ final Vec3 limitedMoveVector = io.papermc.paper.util.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisions); + + if (stepHeight > 0.0 -+ && (this.onGround || (limitedMoveVector.y != moveVector.y && moveVector.y < 0.0)) -+ && (limitedMoveVector.x != moveVector.x || limitedMoveVector.z != moveVector.z)) { -+ Vec3 vec3d2 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(moveVector.x, stepHeight, moveVector.z), currBoundingBox, potentialCollisions); -+ final Vec3 vec3d3 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(moveVector.x, 0.0, moveVector.z), potentialCollisions); ++ && (this.onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0)) ++ && (limitedMoveVector.x != movement.x || limitedMoveVector.z != movement.z)) { ++ Vec3 vec3d2 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, stepHeight, movement.z), currBoundingBox, potentialCollisions); ++ final Vec3 vec3d3 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(movement.x, 0.0, movement.z), potentialCollisions); + + if (vec3d3.y < stepHeight) { -+ final Vec3 vec3d4 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(moveVector.x, 0.0D, moveVector.z), currBoundingBox.move(vec3d3), potentialCollisions).add(vec3d3); ++ final Vec3 vec3d4 = io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(movement.x, 0.0D, movement.z), currBoundingBox.move(vec3d3), potentialCollisions).add(vec3d3); + + if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) { + vec3d2 = vec3d4; @@ -1106,7 +1092,7 @@ index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed + } + + if (vec3d2.horizontalDistanceSqr() > limitedMoveVector.horizontalDistanceSqr()) { -+ return vec3d2.add(io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + moveVector.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisions)); ++ return vec3d2.add(io.papermc.paper.util.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisions)); + } + + return limitedMoveVector; @@ -1119,85 +1105,30 @@ index 74fcc1b45a9e57280da82f7c181530d4183872a5..481e84fda6dccfaf684c922a12fa19ed + // Paper end - optimise collisions } - public static Vec3 collideBoundingBoxHeuristically(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, CollisionContext context, RewindableStream collisions) { -@@ -2320,9 +2395,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, List collisions) { +@@ -2362,11 +2443,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i float f = this.dimensions.width * 0.8F; - AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f); + AABB axisalignedbb = AABB.ofSize(vec3d, (double) f, 1.0E-6D, (double) f); -- return this.level.getBlockCollisions(this, axisalignedbb, (iblockdata, blockposition) -> { -- return iblockdata.isSuffocating(this.level, blockposition); -- }).findAny().isPresent(); +- return this.level.getBlockStates(axisalignedbb).filter(Predicate.not(BlockBehaviour.BlockStateBase::isAir)).anyMatch((iblockdata) -> { +- BlockPos blockposition = new BlockPos(vec3d); +- +- return iblockdata.isSuffocating(this.level, blockposition) && Shapes.joinIsNotEmpty(iblockdata.getCollisionShape(this.level, blockposition).move(vec3d.x, vec3d.y, vec3d.z), Shapes.create(axisalignedbb), BooleanOp.AND); +- }); + // Paper start + return io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this.level, this, axisalignedbb, null, -+ false, false, false, true, (iblockdata, blockposition) -> { -+ return iblockdata.isSuffocating(this.level, blockposition); -+ }); ++ false, false, false, true, (BlockState blockState, BlockPos blockPos) -> { ++ return blockState.isSuffocating(this.level, blockPos); ++ }); + // Paper end } } -diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java -index 2a784a8342e708e0813c7076a2ca8e429446ffd3..f38cd7e2c341ae2fbbe9dd26cf872da9571b416a 100644 ---- a/src/main/java/net/minecraft/world/level/CollisionGetter.java -+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java -@@ -36,28 +36,40 @@ public interface CollisionGetter extends BlockGetter { - return this.isUnobstructed(entity, Shapes.create(entity.getBoundingBox())); - } - -+ // Paper start - optimise collisions -+ default boolean noCollision(Entity entity, AABB box, Predicate filter, boolean loadChunks) { -+ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, loadChunks, false, entity != null, true, null) -+ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, filter); -+ } -+ // Paper end - optimise collisions -+ - default boolean noCollision(AABB box) { -- return this.noCollision((Entity)null, box, (e) -> { -- return true; -- }); -+ // Paper start - optimise collisions -+ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, null, box, null, false, false, false, true, null) -+ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, null, box, null, true, null); -+ // Paper end - optimise collisions - } - - default boolean noCollision(Entity entity) { -- return this.noCollision(entity, entity.getBoundingBox(), (e) -> { -- return true; -- }); -+ // Paper start - optimise collisions -+ AABB box = entity.getBoundingBox(); -+ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) -+ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); -+ // Paper end - optimise collisions - } - - default boolean noCollision(Entity entity, AABB box) { -- return this.noCollision(entity, box, (e) -> { -- return true; -- }); -+ // Paper start - optimise collisions -+ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) -+ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); -+ // Paper end - optimise collisions - } - - default boolean noCollision(@Nullable Entity entity, AABB box, Predicate filter) { -- try { if (entity != null) entity.collisionLoadChunks = true; // Paper -- return this.getCollisions(entity, box, filter).allMatch(VoxelShape::isEmpty); -- } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper -+ // Paper start - optimise collisions -+ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) -+ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, filter); -+ // Paper end - optimise collisions - } - - Stream getEntityCollisions(@Nullable Entity entity, AABB box, Predicate predicate); -diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -index 6124e3a32325e8c74bf839010a79d7c82c49aaff..cffb08bcb63f7ee2d8a163d865d87a9031f19407 100644 ---- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -+++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -@@ -106,7 +106,7 @@ public class CollisionSpliterator extends AbstractSpliterator { +diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java +index d40bbc5ae6b87028a6dde400ea714249792da79a..ca84778200050e0ef7dfeabcaa2e346a6e9cbe12 100644 +--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java ++++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java +@@ -106,7 +106,7 @@ public class BlockCollisions extends AbstractIterator { VoxelShape voxelShape = blockState.getCollisionShape(this.collisionGetter, this.pos, this.context); if (voxelShape == Shapes.block()) { @@ -1206,11 +1137,67 @@ index 6124e3a32325e8c74bf839010a79d7c82c49aaff..cffb08bcb63f7ee2d8a163d865d87a90 continue; } +diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java +index 56d94c94fb0d4dc468bb5d69be655ddd5c6b5360..d7d396ad73866a97cd9f63b34ad8c587f522e713 100644 +--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java ++++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java +@@ -35,31 +35,33 @@ public interface CollisionGetter extends BlockGetter { + return this.isUnobstructed(entity, Shapes.create(entity.getBoundingBox())); + } + ++ // Paper start - optimise collisions ++ default boolean noCollision(Entity entity, AABB box, boolean loadChunks) { ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, loadChunks, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); ++ } ++ // Paper end - optimise collisions ++ + default boolean noCollision(AABB box) { +- return this.noCollision((Entity)null, box); ++ // Paper start - optimise collisions ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, null, box, null, false, false, false, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, null, box, null, true, null); ++ // Paper end - optimise collisions + } + + default boolean noCollision(Entity entity) { +- return this.noCollision(entity, entity.getBoundingBox()); ++ // Paper start - optimise collisions ++ AABB box = entity.getBoundingBox(); ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); ++ // Paper end - optimise collisions + } + + default boolean noCollision(@Nullable Entity entity, AABB box) { +- try { if (entity != null) entity.collisionLoadChunks = true; // Paper +- for(VoxelShape voxelShape : this.getBlockCollisions(entity, box)) { +- if (!voxelShape.isEmpty()) { +- return false; +- } +- } +- } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper +- +- if (!this.getEntityCollisions(entity, box).isEmpty()) { +- return false; +- } else if (entity == null) { +- return true; +- } else { +- VoxelShape voxelShape2 = this.borderCollision(entity, box); +- return voxelShape2 == null || !Shapes.joinIsNotEmpty(voxelShape2, Shapes.create(box), BooleanOp.AND); +- } ++ // Paper start - optimise collisions ++ return !io.papermc.paper.util.CollisionUtil.getCollisionsForBlocksOrWorldBorder(this, entity, box, null, false, false, entity != null, true, null) ++ && !io.papermc.paper.util.CollisionUtil.getEntityHardCollisions(this, entity, box, null, true, null); ++ // Paper end - optimise collisions + } + + List getEntityCollisions(@Nullable Entity entity, AABB box); diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java -index 94130509e3a7980c378cc95c46821cf0fc753ce6..7224c56e8a68870364c6538c82c04f371b74aabd 100644 +index 30276959c0119813c27ee3f98e237c93236e5b39..6df710cecea9a5c91ccf8bdaec60bdc88a601777 100644 --- a/src/main/java/net/minecraft/world/level/EntityGetter.java +++ b/src/main/java/net/minecraft/world/level/EntityGetter.java -@@ -49,7 +49,7 @@ public interface EntityGetter { +@@ -50,7 +50,7 @@ public interface EntityGetter { return true; } else { for(Entity entity2 : this.getEntities(entity, shape.bounds())) { @@ -1219,20 +1206,20 @@ index 94130509e3a7980c378cc95c46821cf0fc753ce6..7224c56e8a68870364c6538c82c04f37 return false; } } -@@ -66,7 +66,7 @@ public interface EntityGetter { - if (box.getSize() < 1.0E-7D) { - return Stream.empty(); +@@ -68,7 +68,7 @@ public interface EntityGetter { + return List.of(); } else { -- AABB aABB = box.inflate(1.0E-7D); -+ AABB aABB = box.inflate(-1.0E-7D); // Paper - needs to be negated, or else we get things we don't collide with - Predicate hardCollides = (entityx) -> { // Paper - optimise entity hard collisions - if (true || entityx.getBoundingBox().intersects(aABB)) { // Paper - always true - if (entity == null) { + Predicate predicate = entity == null ? EntitySelector.CAN_BE_COLLIDED_WITH : EntitySelector.NO_SPECTATORS.and(entity::canCollideWith); +- List list = this.getEntities(entity, box.inflate(1.0E-7D), predicate); ++ List list = this.getEntities(entity, box.inflate(-1.0E-7D), predicate); // Paper - needs to be negated, or else we get things we don't collide with + if (list.isEmpty()) { + return List.of(); + } else { diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -index 4a7fdea6a5f966db444dc41f7215faa99e3820b3..d87f8d106834678364f8720447d671de60c2454e 100644 +index 1831588b275f11aff37573fead835f6ddabfece1..05c46f3b3bce5225b819d86e6e06729a5093e092 100644 --- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -685,7 +685,7 @@ public abstract class BlockBehaviour { +@@ -726,7 +726,7 @@ public abstract class BlockBehaviour { } this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light @@ -1264,7 +1251,7 @@ index 120498a39b7ca7aee9763084507508d4a1c425aa..68cc6f2a78a06293a29317fda72ab3ee this((double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)(pos.getY() + 1), (double)(pos.getZ() + 1)); } diff --git a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java -index 99427b6130895ddecee8bcf77db72d809c24c375..61f5339042290eeaea54964cd3838d0bf4646cb2 100644 +index cdb785619b4fce3cb7f0b4a996a15fa43de5f4d1..6db47035fe940ef1f78a14cae6103e22aa1a184e 100644 --- a/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java +++ b/src/main/java/net/minecraft/world/phys/shapes/ArrayVoxelShape.java @@ -6,6 +6,9 @@ import java.util.Arrays; @@ -1456,10 +1443,10 @@ index 99427b6130895ddecee8bcf77db72d809c24c375..61f5339042290eeaea54964cd3838d0b + } diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857fb1bf0ed 100644 +index 9176735c08a75854209f24113b0e78332249dc4d..7213acf7ac083e37888eeba28f740c31f1fa0d34 100644 --- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java +++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -@@ -26,16 +26,17 @@ public final class Shapes { +@@ -19,16 +19,17 @@ public final class Shapes { DiscreteVoxelShape discreteVoxelShape = new BitSetDiscreteVoxelShape(1, 1, 1); discreteVoxelShape.fill(0, 0, 0); return new CubeVoxelShape(discreteVoxelShape); @@ -1479,7 +1466,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 } public static VoxelShape box(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { -@@ -47,30 +48,11 @@ public final class Shapes { +@@ -40,30 +41,11 @@ public final class Shapes { } public static VoxelShape create(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { @@ -1512,7 +1499,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 } @VisibleForTesting -@@ -132,6 +114,20 @@ public final class Shapes { +@@ -125,6 +107,20 @@ public final class Shapes { } public static boolean joinIsNotEmpty(VoxelShape shape1, VoxelShape shape2, BooleanOp predicate) { @@ -1533,7 +1520,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 if (predicate.apply(false, false)) { throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); } else { -@@ -285,6 +281,43 @@ public final class Shapes { +@@ -196,6 +192,43 @@ public final class Shapes { } public static VoxelShape getFaceShape(VoxelShape shape, Direction direction) { @@ -1577,7 +1564,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 if (shape == block()) { return block(); } else { -@@ -299,7 +332,7 @@ public final class Shapes { +@@ -210,7 +243,7 @@ public final class Shapes { i = 0; } @@ -1586,7 +1573,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 } } -@@ -324,6 +357,53 @@ public final class Shapes { +@@ -235,6 +268,53 @@ public final class Shapes { } public static boolean faceShapeOccludes(VoxelShape one, VoxelShape two) { @@ -1641,7 +1628,7 @@ index 16bc18cacbf7a23fb744c8a12e7fd8da699b2fea..2fb416bd1d7a9879c13907b7e3c6b857 if (one.isEmpty() && two.isEmpty()) { return false; diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java -index f325d76c79d63629200262a77eab7cdcc9beedfa..0ab742f38f79c150630bb9ba153d92d864aface1 100644 +index c4ca051720f790f5b8eb860b14e268de8557454d..2182afd1b95acf14c55bddfeec17dae0a63e1f00 100644 --- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java @@ -16,11 +16,17 @@ import net.minecraft.world.phys.BlockHitResult;