package com.supaham.commons.bukkit.raytracing; import static com.supaham.commons.bukkit.utils.ReflectionUtils.toNMSVec3D; import com.google.common.base.Preconditions; import com.supaham.commons.bukkit.utils.ReflectionUtils; import com.supaham.commons.bukkit.utils.ReflectionUtils.PackageType; import org.bukkit.World; import org.bukkit.util.Vector; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * RayTracing utility class providing methods such as {@link #rayTraceBlocks(World, Vector, * Vector)}. * * @see #rayTraceBlocks(World, Vector, Vector) * @since 0.3.5 */ public class RayTracing { private static Method rayTraceMethod; static { PackageType nms = PackageType.MINECRAFT_SERVER; Class<?> worldClazz = nms.getClassSafe("World"); rayTraceMethod = ReflectionUtils.getMethod(worldClazz, "rayTrace", nms.getClassSafe("Vec3D"), nms.getClassSafe("Vec3D"), boolean.class, boolean.class, boolean.class); } /** * Returns a {@link MovingObjectPosition} as a result of a ray trace from two {@link Vector}s. * This is equivalent to calling {@link #rayTraceBlocks(World, Vector, Vector, boolean, boolean, * boolean)} with the respective booleans provided by the {@link RayTraceData}. * * @param data ray tracing data * * @return {@link MovingObjectPosition}, nullable */ @Nullable public static MovingObjectPosition rayTraceBlocks(@Nonnull RayTraceData data) { Preconditions.checkNotNull(data, "data cannot be null."); return rayTraceBlocks(data.getWorld(), data.getStart().toVector(), data.getEnd().toVector(), data.isStopOnLiquid(), data.isIgnoreBoundingBox(), data.isReturnLastCollidableBlock()); } /** * Returns a {@link MovingObjectPosition} as a result of a ray trace from two {@link Vector}s. * This is equivalent to calling {@link #rayTraceBlocks(World, Vector, Vector, boolean, boolean, * boolean)} with the booleans as {@code false, false, false}. Meaning the ray trace won't stop * when it hits liquids, it won't ignore non solid blocks' bounding boxes, and it won't return * the last collidable block. * * @param world world to perform ray trace in * @param start starting point of ray trace * @param end end point of ray trace * * @return {@link MovingObjectPosition}, nullable */ @Nullable public static MovingObjectPosition rayTraceBlocks(World world, Vector start, Vector end) { return rayTraceBlocks(world, start, end, false, false, false); } /** * Returns a {@link MovingObjectPosition} as a result of a ray trace from two {@link Vector}s. * This is equivalent to calling {@link #rayTraceBlocks(World, Vector, Vector, boolean, boolean, * boolean)} with the last two booleans as {@code false, false}. Meaning the ray trace won't * ignore non solid blocks' bounding boxes, and it won't return the last collidable block. * * @param world world to perform ray trace in * @param start starting point of ray trace * @param end end point of ray trace * @param stopOnLiquid whether to stop on liquid * * @return {@link MovingObjectPosition}, nullable */ public static MovingObjectPosition rayTraceBlocks(World world, Vector start, Vector end, boolean stopOnLiquid) { return rayTraceBlocks(world, start, end, stopOnLiquid, false, false); } /** * Returns a {@link MovingObjectPosition} as a result of a ray trace from two {@link Vector}s. * <p/> * {@code returnLastCollidableBlock} is useful for ensuring the result be non null. At that point * , it returns the {@code end} point. * * @param world world to perform ray trace in * @param start starting point of ray trace * @param end end point of ray trace * @param stopOnLiquid whether to stop on liquid * @param ignoreBoundingBox whether to ignore non solid blocks' bounding box * @param returnLastCollidableBlock whether to return the last collidable block, returns non null * * @return {@link MovingObjectPosition}, nullable */ public static MovingObjectPosition rayTraceBlocks(World world, Vector start, Vector end, boolean stopOnLiquid, boolean ignoreBoundingBox, boolean returnLastCollidableBlock) { try { Object result = rayTraceMethod.invoke(ReflectionUtils.getHandle(world), toNMSVec3D(start), toNMSVec3D(end), stopOnLiquid, ignoreBoundingBox, returnLastCollidableBlock); return result == null ? null : MovingObjectPosition.from(result); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); return null; } } }