mirror of https://github.com/Minestom/Minestom.git
more mutability
This commit is contained in:
parent
21c8dec051
commit
c063688be0
|
@ -268,6 +268,10 @@ public final class BoundingBox implements Shape {
|
|||
reset(boundingBox, p.x(), p.y(), p.z(), axisMask, (int) axis);
|
||||
}
|
||||
|
||||
public void reset(BoundingBox boundingBox, double x, double y, double z, AxisMask axisMask, double axis) {
|
||||
reset(boundingBox, x, y, z, axisMask, (int) axis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return x <= maxX && y <= maxY && z <= maxZ;
|
||||
|
|
|
@ -28,7 +28,7 @@ public final class Navigator {
|
|||
private Point goalPosition;
|
||||
private final Entity entity;
|
||||
|
||||
// Essentially a double buffer. Wait until a path is done computing before replpacing the old one.
|
||||
// Essentially a double buffer. Wait until a path is done computing before replacing the old one.
|
||||
private PPath computingPath;
|
||||
private PPath path;
|
||||
|
||||
|
@ -66,7 +66,7 @@ public final class Navigator {
|
|||
* @param maxDistance maximum search distance
|
||||
* @param pathVariance how far to search off of the direct path. For open worlds, this can be low (around 20) and for large mazes this needs to be very high.
|
||||
* @param onComplete called when the path has been completed
|
||||
* @return true if a path has been found
|
||||
* @return true if a path is being generated
|
||||
*/
|
||||
public synchronized boolean setPathTo(@Nullable Point point, double minimumDistance, double maxDistance, double pathVariance, @Nullable Runnable onComplete) {
|
||||
final Instance instance = entity.getInstance();
|
||||
|
@ -115,9 +115,8 @@ public final class Navigator {
|
|||
this.nodeGenerator,
|
||||
onComplete);
|
||||
|
||||
final boolean success = computingPath != null;
|
||||
this.goalPosition = success ? point : null;
|
||||
return success;
|
||||
this.goalPosition = point;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
@ -136,7 +135,7 @@ public final class Navigator {
|
|||
path.setState(PPath.PathState.FOLLOWING);
|
||||
// Remove nodes that are too close to the start. Prevents doubling back to hit points that have already been hit
|
||||
for (int i = 0; i < path.getNodes().size(); i++) {
|
||||
if (path.getNodes().get(i).point().sameBlock(entity.getPosition())) {
|
||||
if (isSameBlock(path.getNodes().get(i), entity.getPosition())) {
|
||||
path.getNodes().subList(0, i).clear();
|
||||
break;
|
||||
}
|
||||
|
@ -241,9 +240,12 @@ public final class Navigator {
|
|||
if (path == null) return;
|
||||
|
||||
for (PNode point : path.getNodes()) {
|
||||
Point pos = point.point();
|
||||
var packet = new ParticlePacket(Particle.COMPOSTER, pos.x(), pos.y() + 0.5, pos.z(), 0, 0, 0, 0, 1);
|
||||
var packet = new ParticlePacket(Particle.COMPOSTER, point.x(), point.y() + 0.5, point.z(), 0, 0, 0, 0, 1);
|
||||
entity.sendPacketToViewers(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSameBlock(PNode pNode, Pos position) {
|
||||
return Math.floor(pNode.x()) == position.blockX() && Math.floor(pNode.y()) == position.blockY() && Math.floor(pNode.z()) == position.blockZ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ public class PNode {
|
|||
private double g;
|
||||
private double h;
|
||||
private PNode parent;
|
||||
private Point point;
|
||||
private double pointX;
|
||||
private double pointY;
|
||||
private double pointZ;
|
||||
private int hashCode;
|
||||
|
||||
private int cantor(int a, int b) {
|
||||
|
@ -31,17 +33,23 @@ public class PNode {
|
|||
|
||||
private NodeType type;
|
||||
|
||||
public PNode(@NotNull Point point, double g, double h, @Nullable PNode parent) {
|
||||
this(point, g, h, NodeType.WALK, parent);
|
||||
public PNode(double px, double py, double pz, double g, double h, @Nullable PNode parent) {
|
||||
this(px, py, pz, g, h, NodeType.WALK, parent);
|
||||
}
|
||||
|
||||
public PNode(@NotNull Point point, double g, double h, @NotNull NodeType type, @Nullable PNode parent) {
|
||||
this.point = new Vec(point.x(), point.y(), point.z());
|
||||
public PNode(double px, double py, double pz, double g, double h, @NotNull NodeType type, @Nullable PNode parent) {
|
||||
this.g = g;
|
||||
this.h = h;
|
||||
this.parent = parent;
|
||||
this.hashCode = cantor(point.blockX(), cantor(point.blockY(), point.blockZ()));
|
||||
this.type = type;
|
||||
this.pointX = px;
|
||||
this.pointY = py;
|
||||
this.pointZ = pz;
|
||||
this.hashCode = cantor((int) Math.floor(px), cantor((int) Math.floor(py), (int) Math.floor(pz)));
|
||||
}
|
||||
|
||||
public PNode(Point point, double g, double h, NodeType walk, @Nullable PNode parent) {
|
||||
this(point.x(), point.y(), point.z(), g, h, walk, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,15 +68,37 @@ public class PNode {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "PNode{" +
|
||||
"point=" + point +
|
||||
"point=" + pointX + ", " + pointY + ", " + pointZ +
|
||||
", d=" + (g + h) +
|
||||
", type=" + type +
|
||||
'}';
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Point point() {
|
||||
return point;
|
||||
public double x() {
|
||||
return pointX;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public double y() {
|
||||
return pointY;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public double z() {
|
||||
return pointZ;
|
||||
}
|
||||
|
||||
public int blockX() {
|
||||
return (int) Math.floor(pointX);
|
||||
}
|
||||
|
||||
public int blockY() {
|
||||
return (int) Math.floor(pointY);
|
||||
}
|
||||
|
||||
public int blockZ() {
|
||||
return (int) Math.floor(pointZ);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
@ -102,9 +132,11 @@ public class PNode {
|
|||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void setPoint(@NotNull Point point) {
|
||||
this.point = point;
|
||||
this.hashCode = cantor(point.blockX(), cantor(point.blockY(), point.blockZ()));
|
||||
public void setPoint(double px, double py, double pz) {
|
||||
this.pointX = px;
|
||||
this.pointY = py;
|
||||
this.pointZ = pz;
|
||||
this.hashCode = cantor((int) Math.floor(px), cantor((int) Math.floor(py), (int) Math.floor(pz)));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.minestom.server.entity.pathfinding;
|
||||
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -20,7 +21,7 @@ public class PPath {
|
|||
public Point getNext() {
|
||||
if (index + 1 >= nodes.size()) return null;
|
||||
var current = nodes.get(index + 1);
|
||||
return current.point();
|
||||
return new Vec(current.x(), current.y(), current.z());
|
||||
}
|
||||
|
||||
public void setState(@NotNull PathState newState) {
|
||||
|
@ -67,7 +68,7 @@ public class PPath {
|
|||
Point getCurrent() {
|
||||
if (index >= nodes.size()) return null;
|
||||
var current = nodes.get(index);
|
||||
return current.point();
|
||||
return new Vec(current.x(), current.y(), current.z());
|
||||
}
|
||||
|
||||
void next() {
|
||||
|
|
|
@ -5,7 +5,6 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashBigSet;
|
|||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.pathfinding.generators.NodeGenerator;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -17,20 +16,18 @@ import java.util.concurrent.Executors;
|
|||
|
||||
public class PathGenerator {
|
||||
private static final ExecutorService pool = Executors.newWorkStealingPool();
|
||||
private static final PNode repathNode = new PNode(Pos.ZERO, 0, 0, PNode.NodeType.REPATH, null);
|
||||
private static Comparator<PNode> pNodeComparator = (s1, s2) -> (int) (((s1.g() + s1.h()) - (s2.g() + s2.h())) * 1000);
|
||||
private static final PNode repathNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.REPATH, null);
|
||||
private static final Comparator<PNode> pNodeComparator = (s1, s2) -> (int) (((s1.g() + s1.h()) - (s2.g() + s2.h())) * 1000);
|
||||
|
||||
public static @Nullable PPath generate(@NotNull Instance instance, @NotNull Pos orgStart, @NotNull Point orgTarget, double closeDistance, double maxDistance, double pathVariance, @NotNull BoundingBox boundingBox, boolean isOnGround, @NotNull NodeGenerator generator, @Nullable Runnable onComplete) {
|
||||
public static @NotNull PPath generate(@NotNull Instance instance, @NotNull Pos orgStart, @NotNull Point orgTarget, double closeDistance, double maxDistance, double pathVariance, @NotNull BoundingBox boundingBox, boolean isOnGround, @NotNull NodeGenerator generator, @Nullable Runnable onComplete) {
|
||||
Point start = (!isOnGround && generator.hasGravitySnap())
|
||||
? generator.gravitySnap(instance, orgStart, boundingBox, 100)
|
||||
? orgStart.withY(generator.gravitySnap(instance, orgStart.x(), orgStart.y(), orgStart.z(), boundingBox, 100))
|
||||
: orgStart;
|
||||
|
||||
Point target = (generator.hasGravitySnap())
|
||||
? generator.gravitySnap(instance, orgTarget, boundingBox, 100)
|
||||
? orgTarget.withY(generator.gravitySnap(instance, orgTarget.x(), orgTarget.y(), orgTarget.z(), boundingBox, 100))
|
||||
: Pos.fromPoint(orgTarget);
|
||||
|
||||
if (start == null || target == null) return null;
|
||||
|
||||
PPath path = new PPath(maxDistance, pathVariance, onComplete);
|
||||
pool.submit(() -> computePath(instance, start, target, closeDistance, maxDistance, pathVariance, boundingBox, path, generator));
|
||||
|
||||
|
@ -60,13 +57,13 @@ public class PathGenerator {
|
|||
|
||||
PNode current = open.dequeue();
|
||||
|
||||
var chunk = instance.getChunkAt(current.point());
|
||||
var chunk = instance.getChunkAt(current.x(), current.z());
|
||||
if (chunk == null) continue;
|
||||
if (!chunk.isLoaded()) continue;
|
||||
|
||||
if (((current.g() + current.h()) - straightDistance) > pathVariance) continue;
|
||||
if (!withinDistance(current.point(), start, maxDistance)) continue;
|
||||
if (withinDistance(current.point(), target, closeDistance)) {
|
||||
if (!withinDistance(current, start, maxDistance)) continue;
|
||||
if (withinDistance(current, target, closeDistance)) {
|
||||
open.enqueue(current);
|
||||
break;
|
||||
}
|
||||
|
@ -78,7 +75,7 @@ public class PathGenerator {
|
|||
|
||||
Collection<? extends PNode> found = generator.getWalkable(instance, closed, current, target, boundingBox);
|
||||
found.forEach(p -> {
|
||||
if (p.point().distance(start) <= maxDistance) {
|
||||
if (getDistanceSquared(p.x(), p.y(), p.z(), start) <= (maxDistance * maxDistance)) {
|
||||
open.enqueue(p);
|
||||
closed.add(p);
|
||||
}
|
||||
|
@ -87,7 +84,7 @@ public class PathGenerator {
|
|||
|
||||
PNode current = open.isEmpty() ? null : open.dequeue();
|
||||
|
||||
if (current == null || open.isEmpty() || !withinDistance(current.point(), target, closeDistance)) {
|
||||
if (current == null || open.isEmpty() || !withinDistance(current, target, closeDistance)) {
|
||||
if (closestFoundNodes.isEmpty()) {
|
||||
path.setState(PPath.PathState.INVALID);
|
||||
return;
|
||||
|
@ -115,7 +112,7 @@ public class PathGenerator {
|
|||
}
|
||||
|
||||
var lastNode = path.getNodes().get(path.getNodes().size() - 1);
|
||||
if (lastNode.point().distance(target) > closeDistance) {
|
||||
if (getDistanceSquared(lastNode.x(), lastNode.y(), lastNode.z(), target) > (closeDistance * closeDistance)) {
|
||||
path.setState(PPath.PathState.BEST_EFFORT);
|
||||
return;
|
||||
}
|
||||
|
@ -125,7 +122,14 @@ public class PathGenerator {
|
|||
path.setState(PPath.PathState.COMPUTED);
|
||||
}
|
||||
|
||||
private static boolean withinDistance(Point point, Point target, double closeDistance) {
|
||||
return point.distanceSquared(target) < (closeDistance * closeDistance);
|
||||
private static boolean withinDistance(PNode point, Point target, double closeDistance) {
|
||||
return getDistanceSquared(point.x(), point.y(), point.z(), target) < (closeDistance * closeDistance);
|
||||
}
|
||||
|
||||
private static double getDistanceSquared(double x, double y, double z, Point target) {
|
||||
double dx = x - target.x();
|
||||
double dy = y - target.y();
|
||||
double dz = z - target.z();
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@ package net.minestom.server.entity.pathfinding.generators;
|
|||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.pathfinding.PNode;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -18,7 +17,7 @@ public class FlyingNodeGenerator implements NodeGenerator {
|
|||
@Override
|
||||
public @NotNull Collection<? extends PNode> getWalkable(@NotNull Instance instance, @NotNull Set<PNode> visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) {
|
||||
Collection<PNode> nearby = new ArrayList<>();
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, current);
|
||||
|
||||
int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1);
|
||||
if (stepSize < 1) stepSize = 1;
|
||||
|
@ -28,29 +27,43 @@ public class FlyingNodeGenerator implements NodeGenerator {
|
|||
if (x == 0 && z == 0) continue;
|
||||
double cost = Math.sqrt(x * x + z * z) * 0.98;
|
||||
|
||||
Point currentLevelPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() + 0.5);
|
||||
Point upPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() + 1 + 0.5);
|
||||
Point downPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() - 1 + 0.5);
|
||||
double currentLevelPointX = current.blockX() + 0.5 + x;
|
||||
double currentLevelPointY = current.blockY() + 0.5;
|
||||
double currentLevelPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
var nodeWalk = createFly(instance, currentLevelPoint, boundingBox, cost, current, goal, visited);
|
||||
double upPointX = current.blockX() + 0.5 + x;
|
||||
double upPointY = current.blockY() + 1 + 0.5;
|
||||
double upPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
double downPointX = current.blockX() + 0.5 + x;
|
||||
double downPointY = current.blockY() - 1 + 0.5;
|
||||
double downPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
var nodeWalk = createFly(instance, new Vec(currentLevelPointX, currentLevelPointY, currentLevelPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk);
|
||||
|
||||
var nodeJump = createFly(instance, upPoint, boundingBox, cost, current, goal, visited);
|
||||
var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
|
||||
|
||||
var nodeFall = createFly(instance, downPoint, boundingBox, cost, current, goal, visited);
|
||||
var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall);
|
||||
}
|
||||
}
|
||||
|
||||
// Straight up
|
||||
Point upPoint = current.point().withY(current.point().blockY() + 1 + 0.5);
|
||||
var nodeJump = createFly(instance, upPoint, boundingBox, 2, current, goal, visited);
|
||||
double upPointX = current.x();
|
||||
double upPointY = current.blockY() + 1 + 0.5;
|
||||
double upPointZ = current.z();
|
||||
|
||||
var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, 2, current, goal, visited);
|
||||
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
|
||||
|
||||
// Straight down
|
||||
Point downPoint = current.point().withY(current.point().blockY() - 1 + 0.5);
|
||||
var nodeFall = createFly(instance, downPoint, boundingBox, 2, current, goal, visited);
|
||||
double downPointX = current.x();
|
||||
double downPointY = current.blockY() - 1 + 0.5;
|
||||
double downPointZ = current.z();
|
||||
|
||||
var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, 2, current, goal, visited);
|
||||
if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall);
|
||||
|
||||
return nearby;
|
||||
|
@ -64,7 +77,7 @@ public class FlyingNodeGenerator implements NodeGenerator {
|
|||
private PNode createFly(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set<PNode> closed) {
|
||||
var n = newNode(start, cost, point, goal);
|
||||
if (closed.contains(n)) return null;
|
||||
if (!canMoveTowards(instance, start.point(), point, boundingBox)) return null;
|
||||
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null;
|
||||
n.setType(PNode.NodeType.FLY);
|
||||
return n;
|
||||
}
|
||||
|
@ -72,16 +85,16 @@ public class FlyingNodeGenerator implements NodeGenerator {
|
|||
private PNode newNode(PNode current, double cost, Point point, Point goal) {
|
||||
tempNode.setG(current.g() + cost);
|
||||
tempNode.setH(heuristic(point, goal));
|
||||
tempNode.setPoint(point);
|
||||
tempNode.setPoint(point.x(), point.y(), point.z());
|
||||
|
||||
var newNode = tempNode;
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, PNode.NodeType.WALK, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Point gravitySnap(@NotNull Instance instance, @NotNull Point point, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
return null;
|
||||
public double gravitySnap(@NotNull Instance instance, double pointX, double pointY, double pointZ, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
return pointY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@ import java.util.Set;
|
|||
|
||||
public class GroundNodeGenerator implements NodeGenerator {
|
||||
private PNode tempNode = null;
|
||||
private BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator();
|
||||
private final BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator();
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<? extends PNode> getWalkable(@NotNull Instance instance, @NotNull Set<PNode> visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) {
|
||||
Collection<PNode> nearby = new ArrayList<>();
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, current);
|
||||
|
||||
int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1);
|
||||
if (stepSize < 1) stepSize = 1;
|
||||
|
@ -32,18 +32,22 @@ public class GroundNodeGenerator implements NodeGenerator {
|
|||
if (x == 0 && z == 0) continue;
|
||||
double cost = Math.sqrt(x * x + z * z) * 0.98;
|
||||
|
||||
Point floorPoint = new Vec(current.point().blockX() + 0.5 + x, current.point().blockY(), current.point().blockZ() + 0.5 + z);
|
||||
floorPoint = gravitySnap(instance, floorPoint, boundingBox, 5);
|
||||
if (floorPoint == null) continue;
|
||||
double floorPointX = current.blockX() + 0.5 + x;
|
||||
double floorPointY = current.blockY();
|
||||
double floorPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
floorPointY = gravitySnap(instance, floorPointX, floorPointY, floorPointZ, boundingBox, 5);
|
||||
|
||||
var floorPoint = new Vec(floorPointX, floorPointY, floorPointZ);
|
||||
|
||||
var nodeWalk = createWalk(instance, floorPoint, boundingBox, cost, current, goal, visited);
|
||||
if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk);
|
||||
|
||||
for (int i = 1; i <= 1; ++i) {
|
||||
Point jumpPoint = new Vec(current.point().blockX() + 0.5 + x, current.point().blockY() + i, current.point().blockZ() + 0.5 + z);
|
||||
jumpPoint = gravitySnap(instance, jumpPoint, boundingBox, 5);
|
||||
Point jumpPoint = new Vec(current.blockX() + 0.5 + x, current.blockY() + i, current.blockZ() + 0.5 + z);
|
||||
var jumpPointY = gravitySnap(instance, jumpPoint.x(), jumpPoint.y(), jumpPoint.z(), boundingBox, 5);
|
||||
jumpPoint = jumpPoint.withY(jumpPointY);
|
||||
|
||||
if (jumpPoint == null) continue;
|
||||
if (!floorPoint.sameBlock(jumpPoint)) {
|
||||
var nodeJump = createJump(instance, jumpPoint, boundingBox, cost + 0.2, current, goal, visited);
|
||||
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
|
||||
|
@ -64,25 +68,25 @@ public class GroundNodeGenerator implements NodeGenerator {
|
|||
var n = newNode(start, cost, point, goal);
|
||||
if (closed.contains(n)) return null;
|
||||
|
||||
if (Math.abs(point.y() - start.point().y()) > Vec.EPSILON && point.y() < start.point().y()) {
|
||||
if (!canMoveTowards(instance, start.point(), point.withY(start.point().y()), boundingBox)) return null;
|
||||
if (Math.abs(point.y() - start.y()) > Vec.EPSILON && point.y() < start.y()) {
|
||||
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point.withY(start.y()), boundingBox)) return null;
|
||||
n.setType(PNode.NodeType.FALL);
|
||||
} else {
|
||||
if (!canMoveTowards(instance, start.point(), point, boundingBox)) return null;
|
||||
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
private PNode createJump(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set<PNode> closed) {
|
||||
if (Math.abs(point.y() - start.point().y()) < Vec.EPSILON) return null;
|
||||
if (point.y() - start.point().y() > 2) return null;
|
||||
if (point.blockX() != start.point().blockX() && point.blockZ() != start.point().blockZ()) return null;
|
||||
if (Math.abs(point.y() - start.y()) < Vec.EPSILON) return null;
|
||||
if (point.y() - start.y() > 2) return null;
|
||||
if (point.blockX() != start.blockX() && point.blockZ() != start.blockZ()) return null;
|
||||
|
||||
var n = newNode(start, cost, point, goal);
|
||||
if (closed.contains(n)) return null;
|
||||
|
||||
if (pointInvalid(instance, point, boundingBox)) return null;
|
||||
if (pointInvalid(instance, start.point().add(0, 1, 0), boundingBox)) return null;
|
||||
if (pointInvalid(instance, new Vec(start.x(), start.y() + 1, start.z()), boundingBox)) return null;
|
||||
|
||||
n.setType(PNode.NodeType.JUMP);
|
||||
return n;
|
||||
|
@ -91,22 +95,22 @@ public class GroundNodeGenerator implements NodeGenerator {
|
|||
private PNode newNode(PNode current, double cost, Point point, Point goal) {
|
||||
tempNode.setG(current.g() + cost);
|
||||
tempNode.setH(heuristic(point, goal));
|
||||
tempNode.setPoint(point);
|
||||
tempNode.setPoint(point.x(), point.y(), point.z());
|
||||
|
||||
var newNode = tempNode;
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, PNode.NodeType.WALK, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Point gravitySnap(@NotNull Instance instance, @NotNull Point pointOrg, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
double pointX = pointOrg.blockX() + 0.5;
|
||||
double pointY = pointOrg.blockY();
|
||||
double pointZ = pointOrg.blockZ() + 0.5;
|
||||
public double gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
double pointX = (int) Math.floor(pointOrgX) + 0.5;
|
||||
double pointY = (int) Math.floor(pointOrgY);
|
||||
double pointZ = (int) Math.floor(pointOrgZ) + 0.5;
|
||||
|
||||
Chunk c = instance.getChunkAt(pointX, pointZ);
|
||||
if (c == null) return null;
|
||||
if (c == null) return pointY;
|
||||
|
||||
for (int axis = 1; axis <= maxFall; ++axis) {
|
||||
pointIterator.reset(boundingBox, pointX, pointY, pointZ, BoundingBox.AxisMask.Y, -axis);
|
||||
|
@ -115,11 +119,11 @@ public class GroundNodeGenerator implements NodeGenerator {
|
|||
var block = pointIterator.next();
|
||||
|
||||
if (instance.getBlock(block.blockX(), block.blockY(), block.blockZ(), Block.Getter.Condition.TYPE).isSolid()) {
|
||||
return new Vec(pointX, block.blockY() + 1, pointZ);
|
||||
return block.blockY() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Vec(pointX, pointY - maxFall, pointZ);
|
||||
return pointY - maxFall;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import net.minestom.server.entity.pathfinding.PNode;
|
|||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
@ -34,13 +33,16 @@ public interface NodeGenerator {
|
|||
boolean hasGravitySnap();
|
||||
|
||||
/**
|
||||
* Snap the point to the ground.
|
||||
* @param point the point
|
||||
* Snap point to the ground
|
||||
* @param instance the instance
|
||||
* @param pointX the x coordinate
|
||||
* @param pointY the y coordinate
|
||||
* @param pointZ the z coordinate
|
||||
* @param boundingBox the bounding box
|
||||
* @param maxFall the maximum distance to snap down
|
||||
* @return the snapped point
|
||||
* @param maxFall the maximum fall distance
|
||||
* @return the snapped y coordinate
|
||||
*/
|
||||
@Nullable Point gravitySnap(@NotNull Instance instance, @NotNull Point point, @NotNull BoundingBox boundingBox, double maxFall);
|
||||
double gravitySnap(@NotNull Instance instance, double pointX, double pointY, double pointZ, @NotNull BoundingBox boundingBox, double maxFall);
|
||||
|
||||
/**
|
||||
* Check if we can move directly from one point to another
|
||||
|
|
|
@ -2,13 +2,12 @@ package net.minestom.server.entity.pathfinding.generators;
|
|||
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.pathfinding.PNode;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -16,12 +15,12 @@ import java.util.Set;
|
|||
|
||||
public class WaterNodeGenerator implements NodeGenerator {
|
||||
private PNode tempNode = null;
|
||||
private BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator();
|
||||
private final BoundingBox.PointIterator pointIterator = new BoundingBox.PointIterator();
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<? extends PNode> getWalkable(@NotNull Instance instance, @NotNull Set<PNode> visited, @NotNull PNode current, @NotNull Point goal, @NotNull BoundingBox boundingBox) {
|
||||
Collection<PNode> nearby = new ArrayList<>();
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, current);
|
||||
|
||||
int stepSize = (int) Math.max(Math.floor(boundingBox.width() / 2), 1);
|
||||
if (stepSize < 1) stepSize = 1;
|
||||
|
@ -31,38 +30,52 @@ public class WaterNodeGenerator implements NodeGenerator {
|
|||
if (x == 0 && z == 0) continue;
|
||||
double cost = Math.sqrt(x * x + z * z) * 0.98;
|
||||
|
||||
Point currentLevelPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() + 0.5);
|
||||
Point upPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() + 1 + 0.5);
|
||||
Point downPoint = current.point().withX(current.point().blockX() + 0.5 + x).withZ(current.point().blockZ() + 0.5 + z).withY(current.point().blockY() - 1 + 0.5);
|
||||
double currentLevelPointX = current.blockX() + 0.5 + x;
|
||||
double currentLevelPointY = current.blockY();
|
||||
double currentLevelPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
if (instance.getBlock(currentLevelPoint).compare(Block.WATER)) {
|
||||
var nodeWalk = createFly(instance, currentLevelPoint, boundingBox, cost, current, goal, visited);
|
||||
double upPointX = current.blockX() + 0.5 + x;
|
||||
double upPointY = current.blockY() + 1 + 0.5;
|
||||
double upPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
double downPointX = current.blockX() + 0.5 + x;
|
||||
double downPointY = current.blockY() - 1 + 0.5;
|
||||
double downPointZ = current.blockZ() + 0.5 + z;
|
||||
|
||||
if (instance.getBlock((int) Math.floor(currentLevelPointX), (int) Math.floor(currentLevelPointY), (int) Math.floor(currentLevelPointZ)).compare(Block.WATER)) {
|
||||
var nodeWalk = createFly(instance, new Vec(currentLevelPointX, currentLevelPointY, currentLevelPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeWalk != null && !visited.contains(nodeWalk)) nearby.add(nodeWalk);
|
||||
}
|
||||
|
||||
if (instance.getBlock(upPoint).compare(Block.WATER)) {
|
||||
var nodeJump = createFly(instance, upPoint, boundingBox, cost, current, goal, visited);
|
||||
if (instance.getBlock((int) Math.floor(upPointX), (int) Math.floor(upPointY), (int) Math.floor(upPointZ)).compare(Block.WATER)) {
|
||||
var nodeJump = createFly(instance, new Vec(upPointX, upPointY, upPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
|
||||
}
|
||||
|
||||
if (instance.getBlock(downPoint).compare(Block.WATER)) {
|
||||
var nodeFall = createFly(instance, downPoint, boundingBox, cost, current, goal, visited);
|
||||
if (instance.getBlock((int) Math.floor(downPointX), (int) Math.floor(downPointY), (int) Math.floor(downPointZ)).compare(Block.WATER)) {
|
||||
var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, cost, current, goal, visited);
|
||||
if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Straight up
|
||||
Point upPoint = current.point().withY(current.point().blockY() + 1 + 0.5);
|
||||
if (instance.getBlock(upPoint).compare(Block.WATER)) {
|
||||
var nodeJump = createFly(instance, upPoint, boundingBox, 2, current, goal, visited);
|
||||
double upPointX = current.x();
|
||||
double upPointY = current.blockY() + 1 + 0.5;
|
||||
double upPointZ = current.z();
|
||||
|
||||
if (instance.getBlock((int) Math.floor(upPointX), (int) Math.floor(upPointY), (int) Math.floor(upPointZ)).compare(Block.WATER)) {
|
||||
var nodeJump = createFly(instance, new Vec(current.x(), current.y(), current.z()), boundingBox, 2, current, goal, visited);
|
||||
if (nodeJump != null && !visited.contains(nodeJump)) nearby.add(nodeJump);
|
||||
}
|
||||
|
||||
// Straight down
|
||||
Point downPoint = current.point().withY(current.point().blockY() - 1 + 0.5);
|
||||
if (instance.getBlock(downPoint).compare(Block.WATER)) {
|
||||
var nodeFall = createFly(instance, downPoint, boundingBox, 2, current, goal, visited);
|
||||
double downPointX = current.x();
|
||||
double downPointY = current.blockY() - 1 + 0.5;
|
||||
double downPointZ = current.z();
|
||||
|
||||
if (instance.getBlock((int) Math.floor(downPointX), (int) Math.floor(downPointY), (int) Math.floor(downPointZ)).compare(Block.WATER)) {
|
||||
var nodeFall = createFly(instance, new Vec(downPointX, downPointY, downPointZ), boundingBox, 2, current, goal, visited);
|
||||
if (nodeFall != null && !visited.contains(nodeFall)) nearby.add(nodeFall);
|
||||
}
|
||||
|
||||
|
@ -77,7 +90,7 @@ public class WaterNodeGenerator implements NodeGenerator {
|
|||
private PNode createFly(Instance instance, Point point, BoundingBox boundingBox, double cost, PNode start, Point goal, Set<PNode> closed) {
|
||||
var n = newNode(start, cost, point, goal);
|
||||
if (closed.contains(n)) return null;
|
||||
if (!canMoveTowards(instance, start.point(), point, boundingBox)) return null;
|
||||
if (!canMoveTowards(instance, new Vec(start.x(), start.y(), start.z()), point, boundingBox)) return null;
|
||||
n.setType(PNode.NodeType.FLY);
|
||||
return n;
|
||||
}
|
||||
|
@ -85,22 +98,24 @@ public class WaterNodeGenerator implements NodeGenerator {
|
|||
private PNode newNode(PNode current, double cost, Point point, Point goal) {
|
||||
tempNode.setG(current.g() + cost);
|
||||
tempNode.setH(heuristic(point, goal));
|
||||
tempNode.setPoint(point);
|
||||
tempNode.setPoint(point.x(), point.y(), point.z());
|
||||
|
||||
var newNode = tempNode;
|
||||
tempNode = new PNode(Pos.ZERO, 0, 0, PNode.NodeType.WALK, current);
|
||||
tempNode = new PNode(0, 0, 0, 0, 0, PNode.NodeType.WALK, current);
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
public @Nullable Point gravitySnap(@NotNull Instance instance, @NotNull Point point, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
point = new Pos(point.blockX() + 0.5, point.blockY(), point.blockZ() + 0.5);
|
||||
public double gravitySnap(@NotNull Instance instance, double pointOrgX, double pointOrgY, double pointOrgZ, @NotNull BoundingBox boundingBox, double maxFall) {
|
||||
double pointX = (int) Math.floor(pointOrgX) + 0.5;
|
||||
double pointY = (int) Math.floor(pointOrgY);
|
||||
double pointZ = (int) Math.floor(pointOrgZ) + 0.5;
|
||||
|
||||
Chunk c = instance.getChunkAt(point);
|
||||
if (c == null) return null;
|
||||
Chunk c = instance.getChunkAt(pointX, pointZ);
|
||||
if (c == null) return pointY;
|
||||
|
||||
for (int axis = 1; axis <= maxFall; ++axis) {
|
||||
pointIterator.reset(boundingBox, point, BoundingBox.AxisMask.Y, -axis);
|
||||
pointIterator.reset(boundingBox, pointX, pointY, pointZ, BoundingBox.AxisMask.Y, -axis);
|
||||
|
||||
while (pointIterator.hasNext()) {
|
||||
var block = pointIterator.next();
|
||||
|
@ -108,11 +123,11 @@ public class WaterNodeGenerator implements NodeGenerator {
|
|||
var foundBlock = instance.getBlock(block.blockX(), block.blockY(), block.blockZ(), Block.Getter.Condition.TYPE);
|
||||
// Stop falling when water is hit
|
||||
if (foundBlock.isSolid() || foundBlock.compare(Block.WATER)) {
|
||||
return point.withY(block.blockY() + 1);
|
||||
return block.blockY() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return point.withY(point.y() - maxFall);
|
||||
return pointY - maxFall;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@EnvTest
|
||||
public class PathfinderIntegrationTest {
|
||||
|
@ -31,7 +32,7 @@ public class PathfinderIntegrationTest {
|
|||
if (nodes.size() == 0) fail("Path is empty");
|
||||
|
||||
nodes.forEach((node) -> {
|
||||
if (instance.getBlock(node.point()).isSolid()) {
|
||||
if (instance.getBlock(node.blockX(), node.blockY(), node.blockZ()).isSolid()) {
|
||||
fail("Node is inside a block");
|
||||
}
|
||||
});
|
||||
|
@ -106,6 +107,32 @@ public class PathfinderIntegrationTest {
|
|||
validateNodes(nav.getNodes(), i);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBug(Env env) {
|
||||
var i = env.createFlatInstance();
|
||||
i.getWorldBorder().setCenter(0, 0);
|
||||
i.getWorldBorder().setDiameter(10000);
|
||||
|
||||
ChunkUtils.forChunksInRange(0, 0, 10, (x, z) -> {
|
||||
i.loadChunk(x, z).join();
|
||||
});
|
||||
|
||||
var zombie = new LivingEntity(EntityType.ZOMBIE);
|
||||
zombie.setInstance(i, new Pos(43.972731367054266, 40.000000000040735, -39.89155139999369));
|
||||
|
||||
zombie.tick(0);
|
||||
zombie.tick(0);
|
||||
|
||||
Navigator nav = new Navigator(zombie);
|
||||
nav.setPathTo(new Pos(43.5, 40, -41.5));
|
||||
|
||||
while (nav.getState() == PPath.PathState.CALCULATING) {}
|
||||
|
||||
assert(nav.getNodes() != null);
|
||||
|
||||
validateNodes(nav.getNodes(), i);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPFNodeEqual(Env env) {
|
||||
PNode node1 = new PNode(new Pos(0.777, 0, 0), 2, 0, PNode.NodeType.WALK, null);
|
||||
|
@ -167,8 +194,6 @@ public class PathfinderIntegrationTest {
|
|||
nav.setPathTo(new Pos(0, 40, 10));
|
||||
while (nav.getState() == PPath.PathState.CALCULATING) {}
|
||||
|
||||
System.out.println(nav.getNodes());
|
||||
|
||||
assert(nav.getNodes() != null);
|
||||
validateNodes(nav.getNodes(), i);
|
||||
}
|
||||
|
@ -187,7 +212,7 @@ public class PathfinderIntegrationTest {
|
|||
|
||||
var nodeGenerator = new GroundNodeGenerator();
|
||||
|
||||
var snapped = nodeGenerator.gravitySnap(i, new Pos(-140.74433362614695, 40.58268292446131, 18.87966960447388), zombie.getBoundingBox(), 100);
|
||||
assertEquals(new Pos(-140.5, 40.0, 18.5), snapped);
|
||||
var snapped = nodeGenerator.gravitySnap(i, -140.74433362614695, 40.58268292446131, 18.87966960447388, zombie.getBoundingBox(), 100);
|
||||
assertEquals(40.0, snapped);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue