Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RayTraceConfigurationBuilder #11907

Merged
merged 12 commits into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
notTamion marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package io.papermc.paper.raytracing;

import java.util.function.Predicate;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.checkerframework.checker.index.qual.NonNegative;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;

/**
* A builder for configuring a raytrace with a starting location
* and direction.
*/
@NullMarked
public interface PositionedRayTraceConfigurationBuilder {

/**
* Sets the starting location.
*
* @param start the new starting location
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder start(Location start);

/**
* Sets the direction.
*
* @param direction the new direction
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder direction(Vector direction);

/**
* Sets the maximum distance.
*
* @param maxDistance the new maxDistance
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder maxDistance(@NonNegative double maxDistance);

/**
* 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 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
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder ignorePassableBlocks(boolean ignorePassableBlocks);

/**
* Sets the size of the raytrace when looking for entity collisions.
*
* @param raySize the new raytrace size
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder raySize(@NonNegative double raySize);

/**
* Sets the current entity filter when looking for entity collisions.
*
* @param entityFilter predicate for entities the ray can potentially collide with
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder entityFilter(Predicate<? super Entity> entityFilter);

/**
* Sets the current block filter when looking for block collisions.
*
* @param blockFilter predicate for blocks the ray can potentially collide with
* @return a reference to this object
*/
@Contract(value = "_ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder blockFilter(Predicate<? super Block> blockFilter);

/**
* Sets the targets for the rayTrace.
*
* @param first the first target
* @param others the other targets
* @return a reference to this object
*/
@Contract(value = "_, _ -> this", mutates = "this")
PositionedRayTraceConfigurationBuilder targets(RayTraceTarget first, RayTraceTarget... others);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.papermc.paper.raytracing;

/**
* List of Targets a builder can target.
*/
public enum RayTraceTarget {
ENTITY,
BLOCK
}
15 changes: 15 additions & 0 deletions paper-api/src/main/java/org/bukkit/World.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.bukkit;

import java.io.File;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import org.bukkit.generator.ChunkGenerator;

import java.util.ArrayList;
Expand Down Expand Up @@ -1938,6 +1939,20 @@ default Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
@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
* targets.
* <p>
* This may cause loading of chunks! Some implementations may impose
* artificial restrictions on the maximum distance.
*
* @param builderConsumer a consumer to configure the ray trace configuration.
* The received builder is not valid for use outside the consumer
* @return the closest ray trace hit result with either a block or an
* entity, or <code>null</code> if there is no hit
*/
@Nullable RayTraceResult rayTrace(@NotNull Consumer<PositionedRayTraceConfigurationBuilder> builderConsumer);

/**
* Gets the default spawn {@link Location} of this world
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.papermc.paper.raytracing;

import com.google.common.base.Preconditions;
import java.util.EnumSet;
import java.util.OptionalDouble;
import java.util.function.Predicate;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
public class PositionedRayTraceConfigurationBuilderImpl implements PositionedRayTraceConfigurationBuilder {
notTamion marked this conversation as resolved.
Show resolved Hide resolved

public @Nullable Location start;
public @Nullable Vector direction;
public OptionalDouble maxDistance = OptionalDouble.empty();
public FluidCollisionMode fluidCollisionMode = FluidCollisionMode.NEVER;
public boolean ignorePassableBlocks;
public double raySize = 0.0D;
public @Nullable Predicate<? super Entity> entityFilter;
public @Nullable Predicate<? super Block> blockFilter;
public EnumSet<RayTraceTarget> targets = EnumSet.noneOf(RayTraceTarget.class);

@Override
public PositionedRayTraceConfigurationBuilder start(final Location start) {
Preconditions.checkArgument(start != null, "start must not be null");
this.start = start.clone();
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder direction(final Vector direction) {
Preconditions.checkArgument(direction != null, "direction must not be null");
this.direction = direction.clone();
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder maxDistance(final double maxDistance) {
Preconditions.checkArgument(maxDistance >= 0, "maxDistance must be non-negative");
this.maxDistance = OptionalDouble.of(maxDistance);
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder fluidCollisionMode(final FluidCollisionMode fluidCollisionMode) {
Preconditions.checkArgument(fluidCollisionMode != null, "fluidCollisionMode must not be null");
this.fluidCollisionMode = fluidCollisionMode;
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder ignorePassableBlocks(final boolean ignorePassableBlocks) {
this.ignorePassableBlocks = ignorePassableBlocks;
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder raySize(final double raySize) {
Preconditions.checkArgument(raySize >= 0, "raySize must be non-negative");
this.raySize = raySize;
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder entityFilter(final Predicate<? super Entity> entityFilter) {
Preconditions.checkArgument(entityFilter != null, "entityFilter must not be null");
this.entityFilter = entityFilter;
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder blockFilter(final Predicate<? super Block> blockFilter) {
Preconditions.checkArgument(blockFilter != null, "blockFilter must not be null");
this.blockFilter = blockFilter;
return this;
}

@Override
public PositionedRayTraceConfigurationBuilder targets(final RayTraceTarget first, final RayTraceTarget... others) {
Preconditions.checkArgument(first != null, "first must not be null");
Preconditions.checkArgument(others != null, "others must not be null");
this.targets = EnumSet.of(first, others);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import com.google.common.collect.ImmutableMap;
import com.mojang.datafixers.util.Pair;
import io.papermc.paper.FeatureHooks;
import io.papermc.paper.raytracing.RayTraceTarget;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilder;
import io.papermc.paper.raytracing.PositionedRayTraceConfigurationBuilderImpl;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.util.ArrayList;
Expand Down Expand Up @@ -1248,6 +1251,26 @@ public RayTraceResult rayTrace(io.papermc.paper.math.Position start, Vector dire
return blockHit;
}

@Override
public RayTraceResult rayTrace(Consumer<PositionedRayTraceConfigurationBuilder> builderConsumer) {
PositionedRayTraceConfigurationBuilderImpl builder = new PositionedRayTraceConfigurationBuilderImpl();

builderConsumer.accept(builder);
Preconditions.checkArgument(builder.start != null, "Start location cannot be null");
Preconditions.checkArgument(builder.direction != null, "Direction vector cannot be null");
Preconditions.checkArgument(builder.maxDistance.isPresent(), "Max distance must be set");
Preconditions.checkArgument(!builder.targets.isEmpty(), "At least one target");

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.rayTraceEntities(builder.start, builder.direction, maxDistance, builder.raySize, builder.entityFilter);
}
return this.rayTraceBlocks(builder.start, builder.direction, maxDistance, builder.fluidCollisionMode, builder.ignorePassableBlocks, builder.blockFilter);
}

@Override
public List<Player> getPlayers() {
List<Player> list = new ArrayList<Player>(this.world.players().size());
Expand Down
Loading