Add ray tracing and bounding box API

This commit is contained in:
blablubbabc 2018-10-26 19:59:42 +11:00 committed by md_5
parent a835b03538
commit 4a47be6164
6 changed files with 299 additions and 7 deletions

View File

@ -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;
}
}
}

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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);
}
}