From 48c4d681e116f3f92434c3730a1ebd26e8af097b Mon Sep 17 00:00:00 2001 From: Tamion <70228790+notTamion@users.noreply.github.com> Date: Sat, 4 Jan 2025 14:42:50 +0100 Subject: [PATCH] Add RayTraceConfiguration --- .../raytracing/RayTraceConfiguration.java | 206 ++++++++++++++++++ .../RayTraceConfigurationBuilderImpl.java | 93 ++++++++ .../raytracing/RayTraceConfigurationImpl.java | 15 ++ paper-api/src/main/java/org/bukkit/World.java | 25 +++ .../org/bukkit/craftbukkit/CraftWorld.java | 12 + 5 files changed, 351 insertions(+) create mode 100644 paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfiguration.java create mode 100644 paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationBuilderImpl.java create mode 100644 paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationImpl.java diff --git a/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfiguration.java b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfiguration.java new file mode 100644 index 000000000000..293ad30b3653 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfiguration.java @@ -0,0 +1,206 @@ +package io.papermc.paper.raytracing; + +import org.bukkit.FluidCollisionMode; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.function.Predicate; + +/** + * Holds information about how to cast a raytrace. + */ +public interface RayTraceConfiguration { + + /** + * Creates a new builder. + * + * @return the new builder + */ + @NotNull + static Builder builder() { + return new RayTraceConfigurationBuilderImpl(); + } + + /** + * Gets the maximum distance. + * + * @return the maximum distance + */ + double maxDistance(); + + /** + * Gets the FluidCollisionMode when looking for block collisions. + * + * @return the FluidCollisionMode + */ + @Nullable + FluidCollisionMode fluidCollisionMode(); + + /** + * Gets if the raytrace will ignore passable blocks when looking for block collisions. + * + * @return if the raytrace will ignore passable blocks + */ + boolean ignorePassableBlocks(); + + /** + * Gets the size of the raytrace when looking for entity collisions. + * + * @return the raytrace size + */ + double raySize(); + + /** + * Gets the current entity filter. + * + * @return predicate for entities the ray can potentially collide with, or null to consider all entities + */ + @Nullable + Predicate entityFilter(); + + /** + * Gets the current block filter. + * + * @return predicate for blocks the ray can potentially collide with, or null to consider all blocks + */ + @Nullable + Predicate blockFilter(); + + /** + * Gets which RayTraceTargets this configuration was made for. + * + * @return the targets + */ + @NotNull + List targets(); + + /** + * Helps you create a RayTraceConfiguration. + */ + interface Builder { + + /** + * Gets the maximum distance. + * + * @return the maximum distance + */ + double maxDistance(); + + /** + * Sets the maximum distance. + * + * @param maxDistance the new maxDistance + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder maxDistance(double maxDistance); + + /** + * Gets the FluidCollisionMode when looking for block collisions. + * + * @return the FluidCollisionMode + */ + @Nullable + FluidCollisionMode fluidCollisionMode(); + + /** + * Sets the FluidCollisionMode when looking for block collisions. + * + * @param fluidCollisionMode the new FluidCollisionMode + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder fluidCollisionMode(@Nullable FluidCollisionMode fluidCollisionMode); + + /** + * Gets if the raytrace will ignore passable blocks when looking for block collisions. + * + * @return if the raytrace will ignore passable blocks + */ + boolean ignorePassableBlocks(); + + /** + * Gets if the raytrace will ignore passable blocks when looking for block collisions. + * + * @param ignorePassableBlocks if the raytrace should ignore passable blocks + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder ignorePassableBlocks(boolean ignorePassableBlocks); + + /** + * Gets the size of the raytrace when looking for entity collisions. + * + * @return the raytrace size + */ + double raySize(); + + /** + * Sets the size of the raytrace when looking for entity collisions. + * + * @param raySize the new raytrace size + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder raySize(double raySize); + + /** + * Gets the current entity filter when looking for entity collisions. + * + * @return predicate for entities the ray can potentially collide with, or null to consider all entities + */ + @Nullable + Predicate entityFilter(); + + /** + * Sets the current entity filter when looking for entity collisions. + * + * @param entityFilter predicate for entities the ray can potentially collide with, or null to consider all entities + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder entityFilter(@Nullable Predicate entityFilter); + + /** + * Gets the current block filter when looking for block collisions. + * + * @return predicate for blocks the ray can potentially collide with, or null to consider all blocks + */ + @Nullable + Predicate blockFilter(); + + /** + * Sets the current block filter when looking for block collisions. + * + * @param blockFilter predicate for blocks the ray can potentially collide with, or null to consider all blocks + * @return a reference to this object + */ + @NotNull + @Contract("_ -> this") + Builder blockFilter(@Nullable Predicate blockFilter); + + /** + * Builds a configuration based on the provided targets. + * + * @return the configuration + */ + @NotNull + RayTraceConfiguration target(@NotNull Targets first, @NotNull Targets... others); + } + + /** + * List of Targets the builder can target. + */ + enum Targets { + ENTITIES, + BLOCKS + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationBuilderImpl.java b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationBuilderImpl.java new file mode 100644 index 000000000000..b6a9e03023ce --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationBuilderImpl.java @@ -0,0 +1,93 @@ +package io.papermc.paper.raytracing; +import org.bukkit.FluidCollisionMode; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class RayTraceConfigurationBuilderImpl implements RayTraceConfiguration.Builder { + + private double maxDistance; + private FluidCollisionMode fluidCollisionMode = FluidCollisionMode.NEVER; + private boolean ignorePassableBlocks; + private double raySize = 0.0D; + private Predicate entityFilter; + private Predicate blockFilter; + + + @Override + public double maxDistance() { + return maxDistance; + } + + @Override + public @NotNull RayTraceConfiguration.Builder maxDistance(double maxDistance) { + this.maxDistance = maxDistance; + return this; + } + + @Override + public @Nullable FluidCollisionMode fluidCollisionMode() { + return fluidCollisionMode; + } + + @Override + public @NotNull RayTraceConfiguration.Builder fluidCollisionMode(@Nullable FluidCollisionMode fluidCollisionMode) { + this.fluidCollisionMode = fluidCollisionMode; + return this; + } + + @Override + public boolean ignorePassableBlocks() { + return ignorePassableBlocks; + } + + @Override + public @NotNull RayTraceConfiguration.Builder ignorePassableBlocks(boolean ignorePassableBlocks) { + this.ignorePassableBlocks = ignorePassableBlocks; + return this; + } + + @Override + public double raySize() { + return raySize; + } + + @Override + public @NotNull RayTraceConfiguration.Builder raySize(double raySize) { + this.raySize = raySize; + return this; + } + + @Override + public @Nullable Predicate entityFilter() { + return entityFilter; + } + + @Override + public @NotNull RayTraceConfiguration.Builder entityFilter(@Nullable Predicate entityFilter) { + this.entityFilter = entityFilter; + return this; + } + + @Override + public @Nullable Predicate blockFilter() { + return blockFilter; + } + + @Override + public @NotNull RayTraceConfiguration.Builder blockFilter(@Nullable Predicate blockFilter) { + this.blockFilter = blockFilter; + return this; + } + + @Override + public @NotNull RayTraceConfiguration target(@NotNull RayTraceConfiguration.Targets first, @NotNull RayTraceConfiguration.Targets... others) { + List targets = new ArrayList<>(List.of(others)); // Need to make this immutable later + targets.add(first); + return new RayTraceConfigurationImpl(maxDistance, fluidCollisionMode, ignorePassableBlocks, raySize, entityFilter, blockFilter, targets); + } +} diff --git a/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationImpl.java b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationImpl.java new file mode 100644 index 000000000000..ffe49cd1ecc9 --- /dev/null +++ b/paper-api/src/main/java/io/papermc/paper/raytracing/RayTraceConfigurationImpl.java @@ -0,0 +1,15 @@ +package io.papermc.paper.raytracing; + +import org.bukkit.FluidCollisionMode; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.function.Predicate; + +public record RayTraceConfigurationImpl(double maxDistance, @Nullable FluidCollisionMode fluidCollisionMode, + boolean ignorePassableBlocks, double raySize, + @Nullable Predicate entityFilter, @Nullable Predicate blockFilter, + @NotNull List targets) implements RayTraceConfiguration { +} diff --git a/paper-api/src/main/java/org/bukkit/World.java b/paper-api/src/main/java/org/bukkit/World.java index e5e6cdec5fbb..afc011b43831 100644 --- a/paper-api/src/main/java/org/bukkit/World.java +++ b/paper-api/src/main/java/org/bukkit/World.java @@ -1938,6 +1938,31 @@ default Iterable 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 filter, @Nullable Predicate canCollide); // Paper end + /** + * 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 config the configuration + * @return the closest ray trace hit result with either a block or an + * entity, or null if there is no hit + */ + @Nullable RayTraceResult rayTrace(@NotNull Location start, @NotNull Vector direction, @NotNull io.papermc.paper.raytracing.RayTraceConfiguration config); + /** * Gets the default spawn {@link Location} of this world * 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 544305f9d08e..da52ce132189 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableMap; import com.mojang.datafixers.util.Pair; import io.papermc.paper.FeatureHooks; +import io.papermc.paper.raytracing.RayTraceConfiguration; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.io.File; @@ -1246,6 +1247,17 @@ public RayTraceResult rayTrace(io.papermc.paper.math.Position start, Vector dire return blockHit; } + @Override + public @org.jetbrains.annotations.Nullable RayTraceResult rayTrace(Location start, Vector direction, io.papermc.paper.raytracing.RayTraceConfiguration config) { + List targets = config.targets(); + if (targets.contains(io.papermc.paper.raytracing.RayTraceConfiguration.Targets.ENTITIES)) { + if(targets.contains(io.papermc.paper.raytracing.RayTraceConfiguration.Targets.BLOCKS)) + return this.rayTrace(start, direction, config.maxDistance(), config.fluidCollisionMode(), config.ignorePassableBlocks(), config.raySize(), config.entityFilter(), config.blockFilter()); + return this.rayTraceEntities(start, direction, config.maxDistance(), config.raySize(), config.entityFilter()); + } + return this.rayTraceBlocks(start, direction, config.maxDistance(), config.fluidCollisionMode(), config.ignorePassableBlocks(), config.blockFilter()); + } + @Override public List getPlayers() { List list = new ArrayList(this.world.players().size());