Skip to content

Commit

Permalink
Add additional ray trace API
Browse files Browse the repository at this point in the history
  • Loading branch information
TonytheMacaroni committed Feb 23, 2025
1 parent e5a8ee8 commit 7dbd11b
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -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

}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -46,26 +47,25 @@ public interface PositionedRayTraceConfigurationBuilder {

/**
* Sets the FluidCollisionMode when looking for block collisions.
* <p>
* 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
*/
@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.
* <p>
* If collisions with passable blocks are ignored, fluid collisions are
* ignored as well regardless of the fluid collision mode.
* <p>
* 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
Expand Down
4 changes: 4 additions & 0 deletions paper-api/src/main/java/org/bukkit/FluidCollisionMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public enum FluidCollisionMode {
* Only collide with source fluid blocks.
*/
SOURCE_ONLY,
/**
* Collide only with water.
*/
WATER,
/**
* Collide with all fluids.
*/
Expand Down
32 changes: 0 additions & 32 deletions paper-api/src/main/java/org/bukkit/World.java
Original file line number Diff line number Diff line change
Expand Up @@ -1844,13 +1844,6 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
* Performs a ray trace that checks for block collisions using the blocks'
* precise collision shapes.
* <p>
* If collisions with passable blocks are ignored, fluid collisions are
* ignored as well regardless of the fluid collision mode.
* <p>
* 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.
* <p>
* This may cause loading of chunks! Some implementations may impose
* artificial restrictions on the maximum distance.
*
Expand All @@ -1865,18 +1858,10 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> 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.
* <p>
* If collisions with passable blocks are ignored, fluid collisions are
* ignored as well regardless of the fluid collision mode.
* <p>
* 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.
* <p>
* This may cause loading of chunks! Some implementations may impose
* artificial restrictions on the maximum distance.
*
Expand All @@ -1891,7 +1876,6 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
* @return the ray trace hit result, or <code>null</code> 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<? super Block> canCollide);
// Paper end

/**
* Performs a ray trace that checks for both block and entity collisions.
Expand All @@ -1900,13 +1884,6 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
* <code>raySize</code> parameter is only taken into account for entity
* collision checks.
* <p>
* If collisions with passable blocks are ignored, fluid collisions are
* ignored as well regardless of the fluid collision mode.
* <p>
* 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.
* <p>
* This may cause loading of chunks! Some implementations may impose
* artificial restrictions on the maximum distance.
*
Expand All @@ -1926,21 +1903,13 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
@Nullable
public RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, double maxDistance, @NotNull FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, @Nullable Predicate<? super Entity> filter);

// Paper start
/**
* Performs a ray trace that checks for both block and entity collisions.
* <p>
* Block collisions use the blocks' precise collision shapes. The
* <code>raySize</code> parameter is only taken into account for entity
* collision checks.
* <p>
* If collisions with passable blocks are ignored, fluid collisions are
* ignored as well regardless of the fluid collision mode.
* <p>
* 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.
* <p>
* This may cause loading of chunks! Some implementations may impose
* artificial restrictions on the maximum distance.
*
Expand All @@ -1960,7 +1929,6 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
* entity, or <code>null</code> 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<? super Entity> filter, @Nullable Predicate<? super Block> canCollide);
// Paper end

/**
* Performs a ray trace that checks for collisions with the specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<? super Entity> entityFilter;
public @Nullable Predicate<? super Block> blockFilter;
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 13 additions & 5 deletions paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<? super Block> 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<? super Block> 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");
Expand All @@ -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;
Expand All @@ -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);
}
Expand All @@ -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<? super Entity> filter, Predicate<? super Block> 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<? super Entity> filter, Predicate<? super Block> canCollide) {
RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, blockCollisionMode, canCollide);
Vector startVec = null;
double blockHitDistance = maxDistance;

Expand Down Expand Up @@ -1232,11 +1240,11 @@ public RayTraceResult rayTrace(Consumer<PositionedRayTraceConfigurationBuilder>
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
Expand Down

0 comments on commit 7dbd11b

Please sign in to comment.