From 23860da6c2f306fdc598fb0d09132636c7141dfd Mon Sep 17 00:00:00 2001 From: TonytheMacaroni Date: Sat, 4 Nov 2023 18:00:51 -0400 Subject: [PATCH] Add predicate for block when raytracing (#9691) * Add predicate for block data when raytracing blocks * Match based on block, instead of block data * Use instanceof instead of casting * Use Position instead of Location, add overload for rayTraceEntities * Implement requested changes * Invert predicate * Cleanup --- patches/api/0009-Add-Position.patch | 20 ++- ...predicate-for-blocks-when-raytracing.patch | 119 ++++++++++++++++++ patches/server/0009-MC-Utils.patch | 8 +- .../server/0018-Rewrite-chunk-system.patch | 4 +- ...arseException-in-Entity-and-TE-names.patch | 4 +- ...predicate-for-blocks-when-raytracing.patch | 116 +++++++++++++++++ 6 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 patches/api/0444-Add-predicate-for-blocks-when-raytracing.patch create mode 100644 patches/server/1042-Add-predicate-for-blocks-when-raytracing.patch diff --git a/patches/api/0009-Add-Position.patch b/patches/api/0009-Add-Position.patch index 4d6b5f241e..c56e4e0a0e 100644 --- a/patches/api/0009-Add-Position.patch +++ b/patches/api/0009-Add-Position.patch @@ -192,10 +192,10 @@ index 0000000000000000000000000000000000000000..93476aaf8d21efb5a30b6d2cc2eeda81 +} diff --git a/src/main/java/io/papermc/paper/math/Position.java b/src/main/java/io/papermc/paper/math/Position.java new file mode 100644 -index 0000000000000000000000000000000000000000..300da713dcc303b340efad70efe57facf5422964 +index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d9f242740 --- /dev/null +++ b/src/main/java/io/papermc/paper/math/Position.java -@@ -0,0 +1,184 @@ +@@ -0,0 +1,191 @@ +package io.papermc.paper.math; + +import org.bukkit.Location; @@ -273,6 +273,13 @@ index 0000000000000000000000000000000000000000..300da713dcc303b340efad70efe57fac + boolean isFine(); + + /** ++ * Checks if each component of this position is finite. ++ */ ++ default boolean isFinite() { ++ return Double.isFinite(this.x()) && Double.isFinite(this.y()) && Double.isFinite(this.z()); ++ } ++ ++ /** + * Returns a position offset by the specified amounts. + * + * @param x x value to offset @@ -381,7 +388,7 @@ index 0000000000000000000000000000000000000000..300da713dcc303b340efad70efe57fac + } +} diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java -index 7c4db051472fb6a6c6d24092dc6f75487356690a..3b99f359f556e6f2c341d55fa69b7462e69b6546 100644 +index 7c4db051472fb6a6c6d24092dc6f75487356690a..85c342fc50f2fe0ce9a1b3980df9e088c3dea92d 100644 --- a/src/main/java/org/bukkit/Location.java +++ b/src/main/java/org/bukkit/Location.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.Nullable; @@ -393,7 +400,7 @@ index 7c4db051472fb6a6c6d24092dc6f75487356690a..3b99f359f556e6f2c341d55fa69b7462 private Reference world; private double x; private double y; -@@ -706,4 +706,26 @@ public class Location implements Cloneable, ConfigurationSerializable { +@@ -706,4 +706,31 @@ public class Location implements Cloneable, ConfigurationSerializable { } return pitch; } @@ -415,6 +422,11 @@ index 7c4db051472fb6a6c6d24092dc6f75487356690a..3b99f359f556e6f2c341d55fa69b7462 + } + + @Override ++ public boolean isFinite() { ++ return io.papermc.paper.math.FinePosition.super.isFinite() && Float.isFinite(this.getYaw()) && Float.isFinite(this.getPitch()); ++ } ++ ++ @Override + public @NotNull Location toLocation(@NotNull World world) { + return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch()); + } diff --git a/patches/api/0444-Add-predicate-for-blocks-when-raytracing.patch b/patches/api/0444-Add-predicate-for-blocks-when-raytracing.patch new file mode 100644 index 0000000000..9186690f18 --- /dev/null +++ b/patches/api/0444-Add-predicate-for-blocks-when-raytracing.patch @@ -0,0 +1,119 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: TonytheMacaroni +Date: Wed, 6 Sep 2023 19:24:53 -0400 +Subject: [PATCH] Add predicate for blocks when raytracing + + +diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java +index 91eb95b04094394e8dc1e3a3343efc63690c87e4..17d404d6d1a5b46b58d612fca38f17e71adee92e 100644 +--- a/src/main/java/org/bukkit/World.java ++++ b/src/main/java/org/bukkit/World.java +@@ -1714,6 +1714,28 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient + @Nullable + public RayTraceResult rayTraceEntities(@NotNull Location start, @NotNull Vector direction, double maxDistance, double raySize, @Nullable Predicate filter); + ++ // Paper start ++ /** ++ * Performs a ray trace that checks for entity collisions. ++ *

++ * This may not consider entities in currently unloaded chunks. Some ++ * implementations may impose artificial restrictions on the maximum ++ * distance. ++ * ++ * @param start the start position ++ * @param direction the ray direction ++ * @param maxDistance the maximum distance ++ * @param raySize entity bounding boxes will be uniformly expanded (or ++ * shrinked) by this value before doing collision checks ++ * @param filter only entities that fulfill this predicate are considered, ++ * or null to consider all entities ++ * @return the closest ray trace hit result, or null if there ++ * is no hit ++ */ ++ @Nullable ++ public RayTraceResult rayTraceEntities(@NotNull io.papermc.paper.math.Position start, @NotNull Vector direction, double maxDistance, double raySize, @Nullable Predicate filter); ++ // Paper end ++ + /** + * Performs a ray trace that checks for block collisions using the blocks' + * precise collision shapes. +@@ -1777,6 +1799,35 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient + @Nullable + public RayTraceResult rayTraceBlocks(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks); + ++ // Paper start ++ /** ++ * Performs a ray trace that checks for block collisions using the blocks' ++ * precise collision shapes. ++ *

++ * If collisions with passable blocks are ignored, fluid collisions are ++ * ignored as well regardless of the fluid collision mode. ++ *

++ * Portal blocks are only considered passable if the ray starts within ++ * them. Apart from that collisions with portal blocks will be considered ++ * even if collisions with passable blocks are otherwise ignored. ++ *

++ * This may cause loading of chunks! Some implementations may impose ++ * artificial restrictions on the maximum distance. ++ * ++ * @param start the start position ++ * @param direction the ray direction ++ * @param maxDistance the maximum distance ++ * @param fluidCollisionMode the fluid collision mode ++ * @param ignorePassableBlocks whether to ignore passable but collidable ++ * blocks (ex. tall grass, signs, fluids, ..) ++ * @param canCollide predicate for blocks the ray can potentially collide ++ * with, or null to consider all blocks ++ * @return the ray trace hit result, or null if there is no hit ++ */ ++ @Nullable ++ public RayTraceResult rayTraceBlocks(@NotNull io.papermc.paper.math.Position start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, @Nullable Predicate canCollide); ++ // Paper end ++ + /** + * Performs a ray trace that checks for both block and entity collisions. + *

+@@ -1810,6 +1861,43 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient + @Nullable + public RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate filter); + ++ // Paper start ++ /** ++ * Performs a ray trace that checks for both block and entity collisions. ++ *

++ * Block collisions use the blocks' precise collision shapes. The ++ * raySize parameter is only taken into account for entity ++ * collision checks. ++ *

++ * If collisions with passable blocks are ignored, fluid collisions are ++ * ignored as well regardless of the fluid collision mode. ++ *

++ * Portal blocks are only considered passable if the ray starts within them. ++ * Apart from that collisions with portal blocks will be considered even if ++ * collisions with passable blocks are otherwise ignored. ++ *

++ * This may cause loading of chunks! Some implementations may impose ++ * artificial restrictions on the maximum distance. ++ * ++ * @param start the start position ++ * @param direction the ray direction ++ * @param maxDistance the maximum distance ++ * @param fluidCollisionMode the fluid collision mode ++ * @param ignorePassableBlocks whether to ignore passable but collidable ++ * blocks (ex. tall grass, signs, fluids, ..) ++ * @param raySize entity bounding boxes will be uniformly expanded (or ++ * shrinked) by this value before doing collision checks ++ * @param filter only entities that fulfill this predicate are considered, ++ * or null to consider all entities ++ * @param canCollide predicate for blocks the ray can potentially collide ++ * with, or null to consider all blocks ++ * @return the closest ray trace hit result with either a block or an ++ * entity, or null if there is no hit ++ */ ++ @Nullable ++ public RayTraceResult rayTrace(@NotNull io.papermc.paper.math.Position start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate filter, @Nullable Predicate canCollide); ++ // Paper end ++ + /** + * Gets the default spawn {@link Location} of this world + * diff --git a/patches/server/0009-MC-Utils.patch b/patches/server/0009-MC-Utils.patch index c25d9309cd..0850a593eb 100644 --- a/patches/server/0009-MC-Utils.patch +++ b/patches/server/0009-MC-Utils.patch @@ -3480,10 +3480,10 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5 +} diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java new file mode 100644 -index 0000000000000000000000000000000000000000..9572294a50110f2452090da1f32e0a73edc3db05 +index 0000000000000000000000000000000000000000..19708dee02491e74fb3db6e70911e20a57b09769 --- /dev/null +++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -0,0 +1,534 @@ +@@ -0,0 +1,538 @@ +package io.papermc.paper.util; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -3972,6 +3972,10 @@ index 0000000000000000000000000000000000000000..9572294a50110f2452090da1f32e0a73 + return Position.block(vector.getX(), vector.getY(), vector.getZ()); + } + ++ public static Vec3 toVec3(Position position) { ++ return new Vec3(position.x(), position.y(), position.z()); ++ } ++ + public static boolean isEdgeOfChunk(BlockPos pos) { + final int modX = pos.getX() & 15; + final int modZ = pos.getZ() & 15; diff --git a/patches/server/0018-Rewrite-chunk-system.patch b/patches/server/0018-Rewrite-chunk-system.patch index 58890d5471..1a6f07cd6b 100644 --- a/patches/server/0018-Rewrite-chunk-system.patch +++ b/patches/server/0018-Rewrite-chunk-system.patch @@ -15723,7 +15723,7 @@ index cea9c098ade00ee87b8efc8164ab72f5279758f0..197224e31175252d8438a8df585bbb65 + } } diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 9572294a50110f2452090da1f32e0a73edc3db05..dc2ab968ed010289125ac08f0a9ea85af6f6e8bb 100644 +index 19708dee02491e74fb3db6e70911e20a57b09769..395873f89925b495978d151efe1d91da9ad11b0a 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java @@ -4,17 +4,30 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -15769,7 +15769,7 @@ index 9572294a50110f2452090da1f32e0a73edc3db05..dc2ab968ed010289125ac08f0a9ea85a import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; -@@ -528,6 +544,100 @@ public final class MCUtil { +@@ -532,6 +548,100 @@ public final class MCUtil { } } diff --git a/patches/server/0267-Catch-JsonParseException-in-Entity-and-TE-names.patch b/patches/server/0267-Catch-JsonParseException-in-Entity-and-TE-names.patch index 50b2539679..4555657f95 100644 --- a/patches/server/0267-Catch-JsonParseException-in-Entity-and-TE-names.patch +++ b/patches/server/0267-Catch-JsonParseException-in-Entity-and-TE-names.patch @@ -13,10 +13,10 @@ Shulkers) may need to be changed in order for it to re-save properly No more crashing though. diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 82a1a87822e3fc323b92e1aa30ddb0cdb351d89c..d02546b18cb689724887b4e85e8d32a18828a4ad 100644 +index e969a0acf06d9265fa75fc07bb637752df468c11..8240bb085b619f257f8c0a25775e0b15068e440f 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java -@@ -648,4 +648,19 @@ public final class MCUtil { +@@ -652,4 +652,19 @@ public final class MCUtil { public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) { return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status); } diff --git a/patches/server/1042-Add-predicate-for-blocks-when-raytracing.patch b/patches/server/1042-Add-predicate-for-blocks-when-raytracing.patch new file mode 100644 index 0000000000..f31f9be2f6 --- /dev/null +++ b/patches/server/1042-Add-predicate-for-blocks-when-raytracing.patch @@ -0,0 +1,116 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: TonytheMacaroni +Date: Wed, 6 Sep 2023 19:24:16 -0400 +Subject: [PATCH] Add predicate for blocks when raytracing + + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index 799837c172a5f7856c78e6fe2595c575f3058a5e..e2066c52941d20fe01d3381181c3e5bf2d2ccb9a 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -83,6 +83,12 @@ public interface BlockGetter extends LevelHeightAccessor { + + // CraftBukkit start - moved block handling into separate method for use by Block#rayTrace + default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) { ++ // Paper start ++ return clip(raytrace1, blockposition, null); ++ } ++ ++ default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition, java.util.function.Predicate canCollide) { ++ // Paper end + // Paper start - Prevent raytrace from loading chunks + BlockState iblockdata = this.getBlockStateIfLoaded(blockposition); + if (iblockdata == null) { +@@ -92,7 +98,7 @@ public interface BlockGetter extends LevelHeightAccessor { + return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo())); + } + // Paper end +- if (iblockdata.isAir()) return null; // Paper - optimise air cases ++ if (iblockdata.isAir() || (canCollide != null && this instanceof LevelAccessor levelAccessor && !canCollide.test(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, blockposition)))) return null; // Paper - optimise air cases and check canCollide predicate + FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again + Vec3 vec3d = raytrace1.getFrom(); + Vec3 vec3d1 = raytrace1.getTo(); +@@ -108,8 +114,14 @@ public interface BlockGetter extends LevelHeightAccessor { + // CraftBukkit end + + default BlockHitResult clip(ClipContext context) { ++ // Paper start ++ return clip(context, (java.util.function.Predicate) null); ++ } ++ ++ default BlockHitResult clip(ClipContext context, java.util.function.Predicate canCollide) { ++ // Paper end + return (BlockHitResult) BlockGetter.traverseBlocks(context.getFrom(), context.getTo(), context, (raytrace1, blockposition) -> { +- return this.clip(raytrace1, blockposition); // CraftBukkit - moved into separate method ++ return this.clip(raytrace1, blockposition, canCollide); // CraftBukkit - moved into separate method // Paper - use method with canCollide predicate + }, (raytrace1) -> { + Vec3 vec3d = raytrace1.getFrom().subtract(raytrace1.getTo()); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 5e350fa39f47d54f6048ea89c1317759f122b8ae..746f88db6b78b3c8ec372bfaacb26ec98f3b1163 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1118,9 +1118,15 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, double raySize, Predicate filter) { ++ // Paper start ++ return rayTraceEntities((io.papermc.paper.math.Position) start, direction, maxDistance, raySize, filter); ++ } ++ ++ public RayTraceResult rayTraceEntities(io.papermc.paper.math.Position start, Vector direction, double maxDistance, double raySize, Predicate filter) { + Preconditions.checkArgument(start != null, "Location start cannot be null"); +- Preconditions.checkArgument(this.equals(start.getWorld()), "Location start cannot be in a different world"); +- start.checkFinite(); ++ Preconditions.checkArgument(!(start instanceof Location location) || this.equals(location.getWorld()), "Location start cannot be in a different world"); ++ Preconditions.checkArgument(start.isFinite(), "Location start is not finite"); ++ // Paper end + + Preconditions.checkArgument(direction != null, "Vector direction cannot be null"); + direction.checkFinite(); +@@ -1170,9 +1176,16 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks) { ++ // Paper start ++ return this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks, null); ++ } ++ ++ @Override ++ public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, Predicate canCollide) { + Preconditions.checkArgument(start != null, "Location start cannot be null"); +- Preconditions.checkArgument(this.equals(start.getWorld()), "Location start cannot be in a different world"); +- start.checkFinite(); ++ Preconditions.checkArgument(!(start instanceof Location location) || this.equals(location.getWorld()), "Location start cannot be in a different world"); ++ Preconditions.checkArgument(start.isFinite(), "Location start is not finite"); ++ // Paper end + + Preconditions.checkArgument(direction != null, "Vector direction cannot be null"); + direction.checkFinite(); +@@ -1185,16 +1198,23 @@ public class CraftWorld extends CraftRegionAccessor implements World { + } + + Vector dir = direction.clone().normalize().multiply(maxDistance); +- Vec3 startPos = CraftLocation.toVec3D(start); ++ Vec3 startPos = io.papermc.paper.util.MCUtil.toVec3(start); // Paper + Vec3 endPos = startPos.add(dir.getX(), dir.getY(), dir.getZ()); +- HitResult nmsHitResult = this.getHandle().clip(new ClipContext(startPos, endPos, ignorePassableBlocks ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE, CraftFluidCollisionMode.toNMS(fluidCollisionMode), null)); ++ HitResult nmsHitResult = this.getHandle().clip(new ClipContext(startPos, endPos, ignorePassableBlocks ? ClipContext.Block.COLLIDER : ClipContext.Block.OUTLINE, CraftFluidCollisionMode.toNMS(fluidCollisionMode), null), canCollide); // Paper - use method with canCollide predicate + + return CraftRayTraceResult.fromNMS(this, nmsHitResult); + } + + @Override + public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, Predicate filter) { +- RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks); ++ // Paper start ++ return this.rayTrace(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks, raySize, filter, null); ++ } ++ ++ @Override ++ public RayTraceResult rayTrace(io.papermc.paper.math.Position start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, Predicate filter, Predicate canCollide) { ++ RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks, canCollide); ++ // Paper end + Vector startVec = null; + double blockHitDistance = maxDistance; +