RayCastUtility icon

RayCastUtility -----

A small set of utility classes for handling various raycasting functions



Version Independent Ray-Casting Utility

A Note on Versions:
Theoretically speaking, this utility should work for version 1.8 up to and including 1.19. If this is not the case, please make an issue on my GitHub repository and specify your server version and any available server logs or error stack traces. Thanks!

This is a very, very niche resource because I doubt much of anyone needs to use ray-casting for their plugins. But, I found myself needing it since I am programming plugins that are version compatible from 1.8-1.19. In 1.8 there is no Bukkit implemented ray-casting/raytracing. Anyways, if you find yourself in a similar situations, I hope this utility is useful!

What can this thing do?
You can use this utility to ray-cast blocks and entities based on where the player is looking. Want to find out what block the player is looking at (ignoring entities)? Use rayCastBlocks. Want to find what entity the player is looking at? Use rayCastEntities. Want to find out whether the player is looking at a block or entity? Use rayCast. You can specify a maximum distance and a specific precision. I have a bunch of testing information in the class javadoc on specific precisions and it's performance impact. This doesn't necessarily apply to players though! You can also raytrace from the eyes of any LivingEntity! If you'd like to run code for every step of the ray-cast you can use executeStepByStep. This can be used for some cool stuff like Laser Eyes! (See below)

Levels of Precision
Inaccurate Block Precision:
The check location will move 1 block ahead for every check. It's very innacurate and could lead to some issues if you need preciseness. If you're, say, replacing the block the player's looking at, I'd say this would work most of the time.
Inaccurate Entity Precision: The check location will move 0.25 blocks ahead for every check. This is also very innacurate. I can't think of what I'd use it for, but it might be useful for say a `low performance server` option or something.
Semi-Accurate Block Precision: The check location will move 0.5 blocks ahead for every check. This is pretty reliable except for non-full blocks.
Semi-Accurate Entity Precision: The check location will move 0.125 blocks ahead for every check. This is definitely more reliable but can still goof up some times, especially when looking at entities where the hitbox is thinnest.
Accurate Block Precision (Recommended): The check location will move 0.25 blocks ahead for every check. This one has the best performance and I would say this is very accurate for general purpose use.
Accurate Entity Precision: The check location will move 0.05 blocks ahead for every check. This is pretty much accurate unless you're looking at an entity with a small hitbox or at an odd angle.
Precise Block Precision: The check location will move 0.1 blocks ahead for every check. Guareenteed to get the correct block every time. Anti-cheat grade.
Precise Entity Precision (Recommended): The check location will move 0.01 blocks ahead for every check. Guarenteed to get the correct entity no matter the angle or hit box size. Anti-cheat grade.

Examples
Discrepency between what player breaks and what player is looking at.
Code (Java):
public void onBreak (BlockBreakEvent event ) {
    Block block = event. getBlock ( ) ;
    BlockRayCastResult result = RayCastUtility. rayCastBlocks (event. getPlayer ( ), 6, true, RayCastUtility. Precision. PRECISE_BLOCK ) ;
    if ( !result. getBlock ( ). equals (block ) ) {
       event. setCancelled ( true ) ;
        // That's not the block you're looking at!
    }
}
 


Set the block player is looking at to a diamond block
Code (Java):
BlockRayCastResult result = RayCastUtility. rayCastBlocks (event. getPlayer ( ), 10, true, RayCastUtility. Precision. ACCURATE_BLOCK ) ;
result. getBlock ( ). setType (Material. DIAMOND_BLOCK ) ;


Kill the entity the player is looking
Code (Java):
EntityRayCastResult result = RayCastUtility. rayCastEntities (event. getPlayer ( ), 10, true, RayCastUtility. Precision. ACCURATE_BLOCK ) ;
result. getEntity ( ). remove ( ) ;
//Shoot off some fireworks and call it laser eyes or something


Set off TnT at whatever the player is looking at
Code (Java):
RayCastResult result = RayCastUtility. rayCast (event. getPlayer ( ), 10, true, RayCastUtility. Precision. ACCURATE_BLOCK ) ;
if (result. getType ( ) == ResultType. BLOCK ) {
    Block block = (Block ) result. get ( ) ;
    Location loc = block. getLocation ( ) ;
    TNTPrimed tnt = (TNTPrimed ) entity. getWorld ( ). spawnEntity (loc, EntityType. PRIMED_TNT ) ;
    tnt. setFuseTicks ( 80 ) ;
} else if (result. getType ( ) == ResultType. ENTITY ) {
    Entity entity = ( Entity ) result. get ( ) ;
    Location loc = entity. getLocation ( ) ;
    TNTPrimed tnt = (TNTPrimed ) entity. getWorld ( ). spawnEntity (loc, EntityType. PRIMED_TNT ) ;
    tnt. setFuseTicks ( 80 ) ;
}

Spawning TnT every step (Doesn't do anything on ray-cast finish)
Code (Java):
RayCastUtility. executeStepByStep (player, 20, true, 0.5D, true, stepLocation -> {
    TNTPrimed tnt = (TNTPrimed ) stepLocation. getWorld ( ). spawnEntity (stepLocation, EntityType. PRIMED_TNT ) ;
    tnt. setFuseTicks ( 60 ) ;
}, null ) ;

Spawning TnT every step (Sets a block the raytrace hits to obsidian)
Code (Java):
RayCastUtility. executeStepByStep (player, 20, true, 0.5D, true, stepLocation -> {
    TNTPrimed tnt = (TNTPrimed ) stepLocation. getWorld ( ). spawnEntity (stepLocation, EntityType. PRIMED_TNT ) ;
    tnt. setFuseTicks ( 60 ) ;
}, raycastFinishResult -> {
    if (result. getType ( ) == ResultType. BLOCK ) {
        Block block = (Block ) result. get ( ) ;
        block. setType (Material. OBSIDIAN ) ;
    }
} ) ;

Laser Eyes
Code (Java):
RayCastUtility. executeStepByStep (player, 20, true, 0.5D, true, stepLocation -> {
    stepLocation. getWorld ( ). spawnParticle (Particle. CRIT, stepLocation. getX ( ), stepLocation. getY ( ), stepLocation. getZ ( ), 1 ) ;
}, raycastFinishResult -> {
    if (raycastFinishResult. getType ( ) == ResultType. BLOCK ) {
        Location blockLoc = ( (Block ) raycastFinishResult. get ( ) ). getLocation ( ) ;
        blockLoc. getWorld ( ). createExplosion (blockLoc. getX ( ), blockLoc. getY ( ), blockLoc. getZ ( ), 2.5F, false, true ) ;
    } else if (raycastFinishResult. getType ( ) == ResultType. ENTITY ) {
        Entity e = ( Entity ) raycastFinishResult. get ( ) ;
        if (e instanceof LivingEntity ) {
            ( (LivingEntity )e ). damage (10D )
        } else {
            e. remove ( ) ;
        }
    }
} ) ;

I hope you got the jist of it. There's a ton of stuff you can do with ray-casting. From party tricks to cheat detection!

Specific Performance Information
I used a 1.16.5 server with 2GB of RAM. I ran each method (rayCast, rayCastEntities, rayCastBlocks) 1,000 times.


Note: Times for entity raycasting vary depending on how many entities are currently loaded.

Block ray-cast: 80.07 ms (0.08 ms/raycast)
Entity Ray-Cast: 395.43 ms (0.40 ms/raycast)
Full Ray-Cast (Block and entity): 437.01 ms (0.44 ms/raycast)

Block ray-cast: 61.64 ms (0.06 ms/raycast)
Entity Ray-Cast: 225.14 ms (0.23 ms/raycast)
Full Ray-Cast (Block and entity): 253.52 ms (0.25 ms/raycast)

Block ray-cast: 71.90 ms (0.07 ms/raycast)
Entity Ray-Cast: 97.18 ms (0.10 ms/raycast)
Full Ray-Cast (Block and entity): 177.97 ms (0.18 ms/raycast)

Block ray-cast: 61.22 ms (0.06 ms/raycast)
Entity Ray-Cast: 71.96 ms (0.07 ms/raycast)
Full Ray-Cast (Block and entity): 120.24 ms (0.12 ms/raycast)

How can I get this in my project?
You can download the jar and add it as a dependency. (Source code is included with JavaDoc!)
Or you can use Gradle and Maven.

Code (Text):
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
        implementation 'com.github.YeetmanLord:RayCastUtility:<Release Tag>'
}

Code (Text):
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>
<dependency>
    <groupId>com.github.YeetmanLord</groupId>
    <artifactId>RayCastUtility</artifactId>
    <version>1.1.0</version>
</dependency>


Warning! This code uses NMS.

This means that it is possible for this code not to work. I am fairly confident that this code should work from 1.8.8-1.19. If you see any kind of NoSuchMethodException in the console, or the ray-casting utility doesn't seem to produce results, this is the likely reason. I had to use NMS to get an entity's bounding boxes while ray-casting entities. Anyways this code is still version-independent because of Java Reflection, so at least there's that. TL;DR this has NMS and I haven't checked every single version, so it's possible that some versions don't fully work. Later, I will go and test all major versions from 1.8.8 to 1.19. Please report any issues to the issues page.
Resource Information
Author:
----------
Total Downloads: 145
First Release: Oct 28, 2022
Last Update: Nov 10, 2022
Category: ---------------
All-Time Rating:
3 ratings
Version -----
Released: --------------------
Downloads: ------
Version Rating:
----------------------
-- ratings