diff --git a/paper-api/src/main/java/io/papermc/paper/raytrace/BlockCollisionMode.java b/paper-api/src/main/java/io/papermc/paper/raytrace/BlockCollisionMode.java new file mode 100644 index 000000000000..a2dd14244b17 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/raytrace/BlockCollisionMode.java @@ -0,0 +1,25 @@ +package io.papermc.paper.raytrace; + +/** + * Determines the collision behavior when blocks get hit during ray tracing. + */ +public enum BlockCollisionMode { + + /** + * Use the collision shape. + */ + COLLIDER, + /** + * Use the outline shape. + */ + OUTLINE, + /** + * Use the visual shape. + */ + VISUAL, + /** + * Use the shape of a full block, but only consider blocks tagged with {@link org.bukkit.Tag#FALL_DAMAGE_RESETTING}. + */ + FALL_DAMAGE_RESETTING + +} diff --git a/paper-api/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilder.java b/paper-api/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilder.java index 9d41e5622351..f51110a34d94 100644 --- a/paper-api/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilder.java +++ b/paper-api/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilder.java @@ -1,6 +1,7 @@ package io.papermc.paper.raytracing; import java.util.function.Predicate; +import io.papermc.paper.raytrace.BlockCollisionMode; import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.block.Block; @@ -46,9 +47,6 @@ public interface PositionedRayTraceConfigurationBuilder { /** * Sets the FluidCollisionMode when looking for block collisions. - *

- * If collisions with passable blocks are ignored, fluid collisions are - * ignored as well regardless of the fluid collision mode. * * @param fluidCollisionMode the new FluidCollisionMode * @return a reference to this object @@ -56,16 +54,18 @@ public interface PositionedRayTraceConfigurationBuilder { @Contract(value = "_ -> this", mutates = "this") PositionedRayTraceConfigurationBuilder fluidCollisionMode(FluidCollisionMode fluidCollisionMode); + /** + * Sets the BlockCollisionMode when looking for block collisions. + * + * @param blockCollisionMode the new BlockCollisionMode + * @return a reference to this object + */ + @Contract(value = "_ -> this", mutates = "this") + PositionedRayTraceConfigurationBuilder blockCollisionMode(BlockCollisionMode blockCollisionMode); + /** * Sets whether the raytrace should ignore passable blocks when looking for * block collisions. - *

- * 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. * * @param ignorePassableBlocks if the raytrace should ignore passable blocks * @return a reference to this object diff --git a/paper-api/src/main/java/org/bukkit/FluidCollisionMode.java b/paper-api/src/main/java/org/bukkit/FluidCollisionMode.java index ae28958941d7..d03a604bccf9 100644 --- a/paper-api/src/main/java/org/bukkit/FluidCollisionMode.java +++ b/paper-api/src/main/java/org/bukkit/FluidCollisionMode.java @@ -13,6 +13,10 @@ public enum FluidCollisionMode { * Only collide with source fluid blocks. */ SOURCE_ONLY, + /** + * Collide only with water. + */ + WATER, /** * Collide with all fluids. */ diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index 015d852d5a0c..b39d2c048fd7 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -1844,13 +1844,6 @@ default Iterable audiences() { * 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. * @@ -1865,18 +1858,10 @@ default Iterable audiences() { @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. * @@ -1891,7 +1876,6 @@ default Iterable audiences() { * @return the ray trace hit result, or null if there is no hit */ @Nullable RayTraceResult rayTraceBlocks(io.papermc.paper.math.@NotNull 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. @@ -1900,13 +1884,6 @@ default Iterable audiences() { * 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. * @@ -1926,7 +1903,6 @@ default Iterable audiences() { @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. *

@@ -1934,13 +1910,6 @@ default Iterable audiences() { * 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. * @@ -1960,7 +1929,6 @@ default Iterable audiences() { * entity, or null if there is no hit */ @Nullable RayTraceResult rayTrace(io.papermc.paper.math.@NotNull Position start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate filter, @Nullable Predicate canCollide); - // Paper end /** * Performs a ray trace that checks for collisions with the specified diff --git a/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java b/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java index 3d078858d990..aeb0095ba524 100644 --- a/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java +++ b/paper-server/src/main/java/io/papermc/paper/raytracing/PositionedRayTraceConfigurationBuilderImpl.java @@ -4,6 +4,7 @@ import java.util.EnumSet; import java.util.OptionalDouble; import java.util.function.Predicate; +import io.papermc.paper.raytrace.BlockCollisionMode; import org.bukkit.FluidCollisionMode; import org.bukkit.Location; import org.bukkit.block.Block; @@ -19,7 +20,7 @@ public class PositionedRayTraceConfigurationBuilderImpl implements PositionedRay public @Nullable Vector direction; public OptionalDouble maxDistance = OptionalDouble.empty(); public FluidCollisionMode fluidCollisionMode = FluidCollisionMode.NEVER; - public boolean ignorePassableBlocks; + public BlockCollisionMode blockCollisionMode = BlockCollisionMode.OUTLINE; public double raySize = 0.0D; public @Nullable Predicate entityFilter; public @Nullable Predicate blockFilter; @@ -53,9 +54,16 @@ public PositionedRayTraceConfigurationBuilder fluidCollisionMode(final FluidColl return this; } + @Override + public PositionedRayTraceConfigurationBuilder blockCollisionMode(final BlockCollisionMode blockCollisionMode) { + Preconditions.checkArgument(blockCollisionMode != null, "blockCollisionMode must not be null"); + this.blockCollisionMode = blockCollisionMode; + return this; + } + @Override public PositionedRayTraceConfigurationBuilder ignorePassableBlocks(final boolean ignorePassableBlocks) { - this.ignorePassableBlocks = ignorePassableBlocks; + this.blockCollisionMode = ignorePassableBlocks ? BlockCollisionMode.COLLIDER : BlockCollisionMode.OUTLINE; return this; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java index 2178d65faede..52cdaffd5224 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java @@ -13,6 +13,8 @@ public static Fluid toNMS(FluidCollisionMode fluidCollisionMode) { switch (fluidCollisionMode) { case ALWAYS: return Fluid.ANY; + case WATER: + return Fluid.WATER; case SOURCE_ONLY: return Fluid.SOURCE_ONLY; case NEVER: diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 1439d282167d..e239914253fe 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -1159,6 +1159,10 @@ public RayTraceResult rayTraceBlocks(Location start, Vector direction, double ma @Override public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, Predicate canCollide) { + return this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks ? io.papermc.paper.raytrace.BlockCollisionMode.COLLIDER : io.papermc.paper.raytrace.BlockCollisionMode.OUTLINE, canCollide); + } + + public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, io.papermc.paper.raytrace.BlockCollisionMode blockCollisionMode, Predicate canCollide) { Preconditions.checkArgument(start != null, "Location start cannot be null"); 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"); @@ -1169,6 +1173,7 @@ public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vecto Preconditions.checkArgument(direction.lengthSquared() > 0, "Direction's magnitude (%s) need to be greater than 0", direction.lengthSquared()); Preconditions.checkArgument(fluidCollisionMode != null, "FluidCollisionMode cannot be null"); + Preconditions.checkArgument(blockCollisionMode != null, "BlockCollisionMode cannot be null"); if (maxDistance < 0.0D) { return null; @@ -1177,7 +1182,7 @@ public RayTraceResult rayTraceBlocks(io.papermc.paper.math.Position start, Vecto Vector dir = direction.clone().normalize().multiply(maxDistance); Vec3 startPos = io.papermc.paper.util.MCUtil.toVec3(start); // Paper - Add predicate for blocks when raytracing 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), CollisionContext.empty()), canCollide); // Paper - Add predicate for blocks when raytracing + HitResult nmsHitResult = this.getHandle().clip(new ClipContext(startPos, endPos, ClipContext.Block.values()[blockCollisionMode.ordinal()], CraftFluidCollisionMode.toNMS(fluidCollisionMode), CollisionContext.empty()), canCollide); return CraftRayTraceResult.fromNMS(this, nmsHitResult); } @@ -1190,8 +1195,11 @@ public RayTraceResult rayTrace(Location start, Vector direction, double maxDista @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 - Add predicate for blocks when raytracing + return this.rayTrace(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks ? io.papermc.paper.raytrace.BlockCollisionMode.COLLIDER : io.papermc.paper.raytrace.BlockCollisionMode.OUTLINE, raySize, filter, null); + } + + public RayTraceResult rayTrace(io.papermc.paper.math.Position start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, io.papermc.paper.raytrace.BlockCollisionMode blockCollisionMode, double raySize, Predicate filter, Predicate canCollide) { + RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, blockCollisionMode, canCollide); Vector startVec = null; double blockHitDistance = maxDistance; @@ -1232,11 +1240,11 @@ public RayTraceResult rayTrace(Consumer final double maxDistance = builder.maxDistance.getAsDouble(); if (builder.targets.contains(RayTraceTarget.ENTITY)) { if (builder.targets.contains(RayTraceTarget.BLOCK)) { - return this.rayTrace(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.ignorePassableBlocks, builder.raySize, builder.entityFilter, builder.blockFilter); + return this.rayTrace(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.blockCollisionMode, builder.raySize, builder.entityFilter, builder.blockFilter); } return this.rayTraceEntities(builder.start, builder.direction, maxDistance, builder.raySize, builder.entityFilter); } - return this.rayTraceBlocks(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.ignorePassableBlocks, builder.blockFilter); + return this.rayTraceBlocks(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.blockCollisionMode, builder.blockFilter); } @Override