summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblablubbabc <lukas@wirsindwir.de>2018-10-26 19:59:42 +1100
committermd_5 <git@md-5.net>2018-10-26 20:00:00 +1100
commit4a47be61649e410294b504fede87199167821e99 (patch)
tree5874bf26bd3c933a2b6cd71cd46a3f5b2e22893e
parenta835b035387ca35589e89a221bef88d6b255b046 (diff)
downloadcraftbukkit-4a47be61649e410294b504fede87199167821e99.tar
craftbukkit-4a47be61649e410294b504fede87199167821e99.tar.gz
craftbukkit-4a47be61649e410294b504fede87199167821e99.tar.lz
craftbukkit-4a47be61649e410294b504fede87199167821e99.tar.xz
craftbukkit-4a47be61649e410294b504fede87199167821e99.zip
Add ray tracing and bounding box API
-rw-r--r--src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java24
-rw-r--r--src/main/java/org/bukkit/craftbukkit/CraftWorld.java160
-rw-r--r--src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java48
-rw-r--r--src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java7
-rw-r--r--src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java25
-rw-r--r--src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java42
6 files changed, 299 insertions, 7 deletions
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java b/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java
new file mode 100644
index 00000000..faf2fd4e
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java
@@ -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;
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index cc10e277..16fbf732 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -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);
+ }
+
+ @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);
+ }
- 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(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());
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 1b2a8b63..3dbeb376 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -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);
+ }
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index eb1bf55e..f58e5f70 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -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;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index cc9e48d5..3a15b47d 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -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();
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java b/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java
new file mode 100644
index 00000000..d500824b
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java
@@ -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);
+ }
+}