mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 13:07:06 +01:00
Add ray tracing and bounding box API
This commit is contained in:
parent
a835b03538
commit
4a47be6164
6 changed files with 299 additions and 7 deletions
|
@ -0,0 +1,24 @@
|
|||
package org.bukkit.craftbukkit;
|
||||
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import net.minecraft.server.FluidCollisionOption;
|
||||
|
||||
public class CraftFluidCollisionMode {
|
||||
|
||||
private CraftFluidCollisionMode() {}
|
||||
|
||||
public static FluidCollisionOption toNMS(FluidCollisionMode fluidCollisionMode) {
|
||||
if (fluidCollisionMode == null) return null;
|
||||
|
||||
switch (fluidCollisionMode) {
|
||||
case ALWAYS:
|
||||
return FluidCollisionOption.ALWAYS;
|
||||
case SOURCE_ONLY:
|
||||
return FluidCollisionOption.SOURCE_ONLY;
|
||||
case NEVER:
|
||||
return FluidCollisionOption.NEVER;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import java.util.Iterator;
|
|||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.server.*;
|
||||
|
||||
|
@ -22,6 +23,7 @@ import org.bukkit.Chunk;
|
|||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Difficulty;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.GameRule;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
|
@ -43,6 +45,7 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|||
import org.bukkit.craftbukkit.metadata.BlockMetadataStore;
|
||||
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
import org.bukkit.craftbukkit.util.CraftRayTraceResult;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.minecart.CommandMinecart;
|
||||
|
@ -62,7 +65,9 @@ import org.bukkit.plugin.Plugin;
|
|||
import org.bukkit.plugin.messaging.StandardMessenger;
|
||||
import org.bukkit.potion.PotionData;
|
||||
import org.bukkit.potion.PotionType;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Consumer;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class CraftWorld implements World {
|
||||
|
@ -691,19 +696,162 @@ public class CraftWorld implements World {
|
|||
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(Location location, double x, double y, double z) {
|
||||
if (location == null || !location.getWorld().equals(this)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.getNearbyEntities(location, x, y, z, null);
|
||||
}
|
||||
|
||||
AxisAlignedBB bb = new AxisAlignedBB(location.getX() - x, location.getY() - y, location.getZ() - z, location.getX() + x, location.getY() + y, location.getZ() + z);
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(Location location, double x, double y, double z, Predicate<Entity> filter) {
|
||||
Validate.notNull(location, "Location is null!");
|
||||
Validate.isTrue(this.equals(location.getWorld()), "Location is from different world!");
|
||||
|
||||
BoundingBox aabb = BoundingBox.of(location, x, y, z);
|
||||
return this.getNearbyEntities(aabb, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(BoundingBox boundingBox) {
|
||||
return this.getNearbyEntities(boundingBox, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Entity> getNearbyEntities(BoundingBox boundingBox, Predicate<Entity> filter) {
|
||||
Validate.notNull(boundingBox, "Bounding box is null!");
|
||||
|
||||
AxisAlignedBB bb = new AxisAlignedBB(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ());
|
||||
List<net.minecraft.server.Entity> entityList = getHandle().getEntities((net.minecraft.server.Entity) null, bb, null);
|
||||
List<Entity> bukkitEntityList = new ArrayList<org.bukkit.entity.Entity>(entityList.size());
|
||||
for (Object entity : entityList) {
|
||||
bukkitEntityList.add(((net.minecraft.server.Entity) entity).getBukkitEntity());
|
||||
|
||||
for (net.minecraft.server.Entity entity : entityList) {
|
||||
Entity bukkitEntity = entity.getBukkitEntity();
|
||||
if (filter == null || filter.test(bukkitEntity)) {
|
||||
bukkitEntityList.add(bukkitEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return bukkitEntityList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance) {
|
||||
return this.rayTraceEntities(start, direction, maxDistance, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, double raySize) {
|
||||
return this.rayTraceEntities(start, direction, maxDistance, raySize, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, Predicate<Entity> filter) {
|
||||
return this.rayTraceEntities(start, direction, maxDistance, 0.0D, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, double raySize, Predicate<Entity> filter) {
|
||||
Validate.notNull(start, "Start location is null!");
|
||||
Validate.isTrue(this.equals(start.getWorld()), "Start location is from different world!");
|
||||
start.checkFinite();
|
||||
|
||||
Validate.notNull(direction, "Direction is null!");
|
||||
direction.checkFinite();
|
||||
|
||||
Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!");
|
||||
|
||||
if (maxDistance < 0.0D) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector startPos = start.toVector();
|
||||
Vector dir = direction.clone().normalize().multiply(maxDistance);
|
||||
BoundingBox aabb = BoundingBox.of(startPos, startPos).expandDirectional(dir).expand(raySize);
|
||||
Collection<Entity> entities = this.getNearbyEntities(aabb, filter);
|
||||
|
||||
Entity nearestHitEntity = null;
|
||||
RayTraceResult nearestHitResult = null;
|
||||
double nearestDistanceSq = Double.MAX_VALUE;
|
||||
|
||||
for (Entity entity : entities) {
|
||||
BoundingBox boundingBox = entity.getBoundingBox().expand(raySize);
|
||||
RayTraceResult hitResult = boundingBox.rayTrace(startPos, direction, maxDistance);
|
||||
|
||||
if (hitResult != null) {
|
||||
double distanceSq = startPos.distanceSquared(hitResult.getHitPosition());
|
||||
|
||||
if (distanceSq < nearestDistanceSq) {
|
||||
nearestHitEntity = entity;
|
||||
nearestHitResult = hitResult;
|
||||
nearestDistanceSq = distanceSq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (nearestHitEntity == null) ? null : new RayTraceResult(nearestHitResult.getHitPosition(), nearestHitEntity, nearestHitResult.getHitBlockFace());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance) {
|
||||
return this.rayTraceBlocks(start, direction, maxDistance, FluidCollisionMode.NEVER, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
|
||||
return this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks) {
|
||||
Validate.notNull(start, "Start location is null!");
|
||||
Validate.isTrue(this.equals(start.getWorld()), "Start location is from different world!");
|
||||
start.checkFinite();
|
||||
|
||||
Validate.notNull(direction, "Direction is null!");
|
||||
direction.checkFinite();
|
||||
|
||||
Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!");
|
||||
Validate.notNull(fluidCollisionMode, "Fluid collision mode is null!");
|
||||
|
||||
if (maxDistance < 0.0D) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector dir = direction.clone().normalize().multiply(maxDistance);
|
||||
Vec3D startPos = new Vec3D(start.getX(), start.getY(), start.getZ());
|
||||
Vec3D endPos = new Vec3D(start.getX() + dir.getX(), start.getY() + dir.getY(), start.getZ() + dir.getZ());
|
||||
MovingObjectPosition nmsHitResult = this.getHandle().rayTrace(startPos, endPos, CraftFluidCollisionMode.toNMS(fluidCollisionMode), ignorePassableBlocks, false);
|
||||
|
||||
return CraftRayTraceResult.fromNMS(this, nmsHitResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, Predicate<Entity> filter) {
|
||||
RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks);
|
||||
Vector startVec = null;
|
||||
double blockHitDistance = maxDistance;
|
||||
|
||||
// limiting the entity search range if we found a block hit:
|
||||
if (blockHit != null) {
|
||||
startVec = start.toVector();
|
||||
blockHitDistance = startVec.distance(blockHit.getHitPosition());
|
||||
}
|
||||
|
||||
RayTraceResult entityHit = this.rayTraceEntities(start, direction, blockHitDistance, raySize, filter);
|
||||
if (blockHit == null) {
|
||||
return entityHit;
|
||||
}
|
||||
|
||||
if (entityHit == null) {
|
||||
return blockHit;
|
||||
}
|
||||
|
||||
// Cannot be null as blockHit == null returns above
|
||||
double entityHitDistanceSquared = startVec.distanceSquared(entityHit.getHitPosition());
|
||||
if (entityHitDistanceSquared < (blockHitDistance * blockHitDistance)) {
|
||||
return entityHit;
|
||||
}
|
||||
|
||||
return blockHit;
|
||||
}
|
||||
|
||||
public List<Player> getPlayers() {
|
||||
List<Player> list = new ArrayList<Player>(world.players.size());
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ import java.util.List;
|
|||
|
||||
import net.minecraft.server.*;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
|
@ -18,15 +20,18 @@ import org.bukkit.block.BlockFace;
|
|||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.PistonMoveReaction;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.CraftChunk;
|
||||
import org.bukkit.craftbukkit.CraftFluidCollisionMode;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
import org.bukkit.craftbukkit.util.CraftRayTraceResult;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.BlockVector;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class CraftBlock implements Block {
|
||||
private final net.minecraft.server.GeneratorAccess world;
|
||||
|
@ -602,4 +607,45 @@ public class CraftBlock implements Block {
|
|||
public boolean isPassable() {
|
||||
return this.getData0().getCollisionShape(world, position).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) {
|
||||
Validate.notNull(start, "Start location is null!");
|
||||
Validate.isTrue(this.getWorld().equals(start.getWorld()), "Start location is from different world!");
|
||||
start.checkFinite();
|
||||
|
||||
Validate.notNull(direction, "Direction is null!");
|
||||
direction.checkFinite();
|
||||
Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!");
|
||||
|
||||
Validate.notNull(fluidCollisionMode, "Fluid collision mode is null!");
|
||||
if (maxDistance < 0.0D) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector dir = direction.clone().normalize().multiply(maxDistance);
|
||||
Vec3D startPos = new Vec3D(start.getX(), start.getY(), start.getZ());
|
||||
Vec3D endPos = new Vec3D(start.getX() + dir.getX(), start.getY() + dir.getY(), start.getZ() + dir.getZ());
|
||||
|
||||
// Similar to to nms.World#rayTrace:
|
||||
IBlockData blockData = world.getType(position);
|
||||
Fluid fluid = world.b(position); // PAIL getFluid
|
||||
boolean collidableBlock = blockData.getBlock().d(blockData); // PAIL isCollidable
|
||||
boolean collideWithFluid = CraftFluidCollisionMode.toNMS(fluidCollisionMode).d.test(fluid); // PAIL predicate
|
||||
|
||||
if (!collidableBlock && !collideWithFluid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MovingObjectPosition nmsHitResult = null;
|
||||
if (collidableBlock) {
|
||||
nmsHitResult = net.minecraft.server.Block.a(blockData, world.getMinecraftWorld(), position, startPos, endPos); // PAIL rayTrace
|
||||
}
|
||||
|
||||
if (nmsHitResult == null && collideWithFluid) {
|
||||
nmsHitResult = VoxelShapes.a(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid.f(), 1.0D).a(startPos, endPos, position); // PAIL create, getHeight, rayTrace
|
||||
}
|
||||
|
||||
return CraftRayTraceResult.fromNMS(this.getWorld(), nmsHitResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.bukkit.permissions.PermissionAttachment;
|
|||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.permissions.ServerOperator;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
|
@ -273,6 +274,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|||
return getHandle().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox getBoundingBox() {
|
||||
AxisAlignedBB bb = getHandle().getBoundingBox();
|
||||
return new BoundingBox(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ);
|
||||
}
|
||||
|
||||
public boolean isOnGround() {
|
||||
if (entity instanceof EntityArrow) {
|
||||
return ((EntityArrow) entity).inGround;
|
||||
|
|
|
@ -36,6 +36,7 @@ import net.minecraft.server.MobEffect;
|
|||
import net.minecraft.server.MobEffectList;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.FluidCollisionMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
|
@ -78,6 +79,7 @@ import org.bukkit.potion.PotionEffect;
|
|||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.potion.PotionType;
|
||||
import org.bukkit.util.BlockIterator;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
||||
|
@ -170,6 +172,29 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|||
return getLineOfSight(transparent, maxDistance, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getTargetBlockExact(int maxDistance) {
|
||||
return this.getTargetBlockExact(maxDistance, FluidCollisionMode.NEVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getTargetBlockExact(int maxDistance, FluidCollisionMode fluidCollisionMode) {
|
||||
RayTraceResult hitResult = this.rayTraceBlocks(maxDistance, fluidCollisionMode);
|
||||
return (hitResult != null ? hitResult.getHitBlock() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(double maxDistance) {
|
||||
return this.rayTraceBlocks(maxDistance, FluidCollisionMode.NEVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RayTraceResult rayTraceBlocks(double maxDistance, FluidCollisionMode fluidCollisionMode) {
|
||||
Location eyeLocation = this.getEyeLocation();
|
||||
Vector direction = eyeLocation.getDirection();
|
||||
return this.getWorld().rayTraceBlocks(eyeLocation, direction, maxDistance, fluidCollisionMode, false);
|
||||
}
|
||||
|
||||
public int getRemainingAir() {
|
||||
return getHandle().getAirTicks();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package org.bukkit.craftbukkit.util;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.craftbukkit.block.CraftBlock;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.MovingObjectPosition;
|
||||
import net.minecraft.server.MovingObjectPosition.EnumMovingObjectType;
|
||||
import net.minecraft.server.Vec3D;
|
||||
|
||||
public class CraftRayTraceResult {
|
||||
|
||||
private CraftRayTraceResult() {}
|
||||
|
||||
public static RayTraceResult fromNMS(World world, MovingObjectPosition nmsHitResult) {
|
||||
if (nmsHitResult == null || nmsHitResult.type == EnumMovingObjectType.MISS) return null;
|
||||
|
||||
Vec3D nmsHitPos = nmsHitResult.pos;
|
||||
Vector hitPosition = new Vector(nmsHitPos.x, nmsHitPos.y, nmsHitPos.z);
|
||||
BlockFace hitBlockFace = null;
|
||||
|
||||
if (nmsHitResult.direction != null) {
|
||||
hitBlockFace = CraftBlock.notchToBlockFace(nmsHitResult.direction);
|
||||
}
|
||||
|
||||
if (nmsHitResult.entity != null) {
|
||||
Entity hitEntity = nmsHitResult.entity.getBukkitEntity();
|
||||
return new RayTraceResult(hitPosition, hitEntity, hitBlockFace);
|
||||
}
|
||||
|
||||
Block hitBlock = null;
|
||||
BlockPosition nmsBlockPos = nmsHitResult.a(); // PAIL: getBlockPosition
|
||||
if (nmsBlockPos != null && world != null) {
|
||||
hitBlock = world.getBlockAt(nmsBlockPos.getX(), nmsBlockPos.getY(), nmsBlockPos.getZ());
|
||||
}
|
||||
return new RayTraceResult(hitPosition, hitBlock, hitBlockFace);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue