diff --git a/src/main/java/net/minestom/server/collision/BoundingBox.java b/src/main/java/net/minestom/server/collision/BoundingBox.java index 7087ee595..447d37430 100644 --- a/src/main/java/net/minestom/server/collision/BoundingBox.java +++ b/src/main/java/net/minestom/server/collision/BoundingBox.java @@ -138,6 +138,8 @@ public class BoundingBox { * @return true if the bounding box intersects with the line segment, false otherwise. */ public boolean intersect(double x1, double y1, double z1, double x2, double y2, double z2) { + // originally from http://www.3dkingdoms.com/weekly/weekly.php?a=3 + double x3 = getMinX(); double x4 = getMaxX(); double y3 = getMinY(); @@ -152,12 +154,12 @@ public class BoundingBox { z1 < z3 && z2 < z3 || z1 > z4 && z2 > z4) { return false; } - return isInsideBoxWithAxis(1, getSegmentIntersection(x1 - x3, x2 - x3, x1, y1, z1, x2, y2, z2)) || - isInsideBoxWithAxis(1, getSegmentIntersection(x1 - x4, x2 - x4, x1, y1, z1, x2, y2, z2)) || - isInsideBoxWithAxis(2, getSegmentIntersection(y1 - y3, y2 - y3, x1, y1, z1, x2, y2, z2)) || - isInsideBoxWithAxis(2, getSegmentIntersection(y1 - y4, y2 - y4, x1, y1, z1, x2, y2, z2)) || - isInsideBoxWithAxis(3, getSegmentIntersection(z1 - z3, z2 - z3, x1, y1, z1, x2, y2, z2)) || - isInsideBoxWithAxis(3, getSegmentIntersection(z1 - z4, z2 - z4, x1, y1, z1, x2, y2, z2)); + return isInsideBoxWithAxis(Axis.X, getSegmentIntersection(x1 - x3, x2 - x3, x1, y1, z1, x2, y2, z2)) || + isInsideBoxWithAxis(Axis.X, getSegmentIntersection(x1 - x4, x2 - x4, x1, y1, z1, x2, y2, z2)) || + isInsideBoxWithAxis(Axis.Y, getSegmentIntersection(y1 - y3, y2 - y3, x1, y1, z1, x2, y2, z2)) || + isInsideBoxWithAxis(Axis.Y, getSegmentIntersection(y1 - y4, y2 - y4, x1, y1, z1, x2, y2, z2)) || + isInsideBoxWithAxis(Axis.Z, getSegmentIntersection(z1 - z3, z2 - z3, x1, y1, z1, x2, y2, z2)) || + isInsideBoxWithAxis(Axis.Z, getSegmentIntersection(z1 - z4, z2 - z4, x1, y1, z1, x2, y2, z2)); } /** @@ -190,7 +192,7 @@ public class BoundingBox { ); } - private boolean isInsideBoxWithAxis(int axis, @Nullable Vec intersection) { + private boolean isInsideBoxWithAxis(Axis axis, @Nullable Vec intersection) { if (intersection == null) { return false; } @@ -200,9 +202,9 @@ public class BoundingBox { double y2 = getMaxY(); double z1 = getMinZ(); double z2 = getMaxZ(); - return axis == 1 && intersection.z() > z1 && intersection.z() < z2 && intersection.y() > y1 && intersection.y() < y2 || - axis == 2 && intersection.z() > z1 && intersection.z() < z2 && intersection.x() > x1 && intersection.x() < x2 || - axis == 3 && intersection.x() > x1 && intersection.x() < x2 && intersection.y() > y1 && intersection.y() < y2; + return axis == Axis.X && intersection.z() > z1 && intersection.z() < z2 && intersection.y() > y1 && intersection.y() < y2 || + axis == Axis.Y && intersection.z() > z1 && intersection.z() < z2 && intersection.x() > x1 && intersection.x() < x2 || + axis == Axis.Z && intersection.x() > x1 && intersection.x() < x2 && intersection.y() > y1 && intersection.y() < y2; } /** @@ -417,4 +419,9 @@ public class BoundingBox { this.lastPosition = entityPos; return vecSupplier.get(); } + + private enum Axis { + X, Y, Z + } + } diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 81236b35e..526cf95d4 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -1455,26 +1455,7 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler } } - public Collection getNearbyEntities(double range) { - Instance instance = getInstance(); - if (instance == null) { - return Collections.emptySet(); - } - int minX = ChunkUtils.getChunkCoordinate(position.x() - range); - int maxX = ChunkUtils.getChunkCoordinate(position.x() + range); - int minZ = ChunkUtils.getChunkCoordinate(position.z() - range); - int maxZ = ChunkUtils.getChunkCoordinate(position.z() + range); - List result = new ArrayList<>(); - for (int x = minX; x <= maxX; ++x) { - for (int z = minZ; z <= maxZ; ++z) { - Chunk chunk = instance.getChunk(x, z); - if (chunk != null) { - result.addAll(instance.getChunkEntities(chunk)); - } - } - } - return result; - } + /** * Gets the line of sight of the entity. @@ -1483,11 +1464,16 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler * @return A list of {@link Point poiints} in this entities line of sight */ public List getLineOfSight(int maxDistance) { + Instance instance = getInstance(); + if (instance == null) { + return Collections.emptyList(); + } + List blocks = new ArrayList<>(); - Iterator it = new BlockIterator(this, maxDistance); + var it = new BlockIterator(this, maxDistance); while (it.hasNext()) { final Point position = it.next(); - if (!getInstance().getBlock(position).isAir()) blocks.add(position); + if (!instance.getBlock(position).isAir()) blocks.add(position); } return blocks; } @@ -1501,14 +1487,19 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler * @return if the current entity has line of sight to the given one. */ public boolean hasLineOfSight(Entity entity) { - final var start = getPosition().asVec().add(0D, getEyeHeight(), 0D); - final var end = entity.getPosition().asVec().add(0D, getEyeHeight(), 0D); - final var direction = end.sub(start); + Instance instance = getInstance(); + if (instance == null) { + return false; + } + + final Vec start = getPosition().asVec().add(0D, getEyeHeight(), 0D); + final Vec end = entity.getPosition().asVec().add(0D, getEyeHeight(), 0D); + final Vec direction = end.sub(start); final int maxDistance = (int) Math.ceil(direction.length()); - Iterator it = new BlockIterator(start, direction.normalize(), 0D, maxDistance); + var it = new BlockIterator(start, direction.normalize(), 0D, maxDistance); while (it.hasNext()) { - Block block = getInstance().getBlock(it.next()); + Block block = instance.getBlock(it.next()); if (!block.isAir() && !block.isLiquid()) { return false; } @@ -1520,34 +1511,32 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler * Gets first entity on the line of sight of the current one that matches the given predicate. * * @param range max length of the line of sight of the current entity to be checked. - * @param predicate optional predicate, defaults to "accept any". + * @param predicate optional predicate * @return resulting entity whether there're any, null otherwise. */ - public @Nullable Entity getLineOfSightEntity(double range, @Nullable Predicate predicate) { - var instance = getInstance(); + public @Nullable Entity getLineOfSightEntity(double range, Predicate predicate) { + Instance instance = getInstance(); if (instance == null) { return null; } - var start = new Vec(position.x(), position.y() + getEyeHeight(), position.z()); - var end = start.add(position.direction().mul(range)); + Vec start = new Vec(position.x(), position.y() + getEyeHeight(), position.z()); + Vec end = start.add(position.direction().mul(range)); - var finalPredicate = Objects.requireNonNullElseGet(predicate, () -> e -> true); - - var nearby = getNearbyEntities(range).stream() - .filter(e -> e != this && e.boundingBox.intersect(start, end) && finalPredicate.test(e)) + List nearby = instance.getNearbyEntities(position, range).stream() + .filter(e -> e != this && e.boundingBox.intersect(start, end) && predicate.test(e)) .collect(Collectors.toList()); if (nearby.isEmpty()) { return null; } - var direction = end.sub(start); + Vec direction = end.sub(start); int maxDistance = (int) Math.ceil(direction.length()); double maxVisibleDistanceSquared = direction.lengthSquared(); - Iterator iterator = new BlockIterator(start, direction.normalize(), 0D, maxDistance); + var iterator = new BlockIterator(start, direction.normalize(), 0D, maxDistance); while (iterator.hasNext()) { - var blockPos = iterator.next(); + Point blockPos = iterator.next(); Block block = instance.getBlock(blockPos); if (!block.isAir() && !block.isLiquid()) { maxVisibleDistanceSquared = blockPos.distanceSquared(position); @@ -1557,7 +1546,7 @@ public class Entity implements Viewable, Tickable, TagHandler, PermissionHandler Entity result = null; double minDistanceSquared = 0D; - for (var entity : nearby) { + for (Entity entity : nearby) { double distanceSquared = entity.getDistanceSquared(this); if (result == null || minDistanceSquared > distanceSquared) { result = entity; diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index b509e8bd1..222cee95e 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -489,6 +489,32 @@ public abstract class Instance implements BlockGetter, BlockSetter, Tickable, Ta return Collections.unmodifiableSet(entities); } + /** + * Gets nearby entities to the given position. + * + * @param point position to look at + * @param range max range from the given point to collect entities at + * @return entities that are not further than the specified distance from the transmitted position. + */ + public @NotNull Collection getNearbyEntities(@NotNull Point point, double range) { + int minX = ChunkUtils.getChunkCoordinate(point.x() - range); + int maxX = ChunkUtils.getChunkCoordinate(point.x() + range); + int minZ = ChunkUtils.getChunkCoordinate(point.z() - range); + int maxZ = ChunkUtils.getChunkCoordinate(point.z() + range); + List result = new ArrayList<>(); + synchronized (entitiesLock) { + for (int x = minX; x <= maxX; ++x) { + for (int z = minZ; z <= maxZ; ++z) { + Chunk chunk = getChunk(x, z); + if (chunk != null) { + result.addAll(getChunkEntities(chunk)); + } + } + } + } + return result; + } + @Override public @Nullable Block getBlock(int x, int y, int z, @NotNull Condition condition) { final Chunk chunk = getChunkAt(x, z);