Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,13 @@ current route until the replacement lands, so a chased target never stands
still.

`controller.state()` reports progress: `COMPLETED` arrived, `PARTIAL` ran out
short of the goal, `STUCK` cannot walk the route it has.
short of the goal, `STUCK` cannot walk the route it has. Chunks that are not
loaded count as solid, so a mob heading into them stops at the edge with
`controller.routeStop() == PathStop.TERRAIN_UNLOADED`; call `moveTo` again once
they load.

Need a fixed profile, another speed, or direct movement? Use
`navigation.controllerBuilder(mob)`.

That is the whole everyday API; see the [wiki](https://github.com/AtlasEngineCa/Pathfinding/wiki).

Expand Down
8 changes: 7 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jmh {
}

group = "ca.atlasengine"
version = "10.3.0"
version = "11.0.0"

repositories {
mavenCentral()
Expand Down Expand Up @@ -73,6 +73,12 @@ tasks.withType<JavaCompile>().configureEach {
options.release = 25
}

// The module exports only its API packages. A public signature naming a type
// from a non-exported package compiles here but not for a modular consumer.
tasks.named<JavaCompile>("compileJava") {
options.compilerArgs.addAll(listOf("-Xlint:exports", "-Werror"))
}

// Runnable demo server. It compiles against the published packages only, so
// `build` failing here means the public API moved under an integration.
val examples = sourceSets.create("examples")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ca.atlasengine.pathfinding.examples;

import net.kyori.adventure.text.Component;
import ca.atlasengine.pathfinding.EntityNavigationController;
import ca.atlasengine.pathfinding.NavigationState;
import ca.atlasengine.pathfinding.PathDebugRenderer;
import net.kyori.adventure.text.Component;
import net.minestom.server.component.DataComponents;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.EntityCreature;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ca.atlasengine.pathfinding.examples;

import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.NavigationSystem;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
Expand Down Expand Up @@ -42,7 +42,8 @@ public NavigationSystem navigation() {
public DemoMob spawn(EntityType type, String label, Pos at) {
DemoMob mob = new DemoMob(type, label);
mob.setInstance(instance, at).join();
return track(mob.navigateWith(navigation.controller(mob, SPEED)));
return track(mob.navigateWith(navigation.controllerBuilder(mob)
.movementPerTick(SPEED).build()));
}

/** Spawns a mob navigating with an explicitly composed profile. */
Expand All @@ -51,7 +52,8 @@ public DemoMob spawn(EntityType type, String label, Pos at,
DemoMob mob = new DemoMob(type, label);
mob.setInstance(instance, at).join();
return track(mob.navigateWith(
navigation.controller(mob, profile, SPEED)));
navigation.controllerBuilder(mob).profile(profile)
.movementPerTick(SPEED).build()));
}

/** Spawns a mob that pursues {@code quarry} through the shared mesh. */
Expand Down
23 changes: 12 additions & 11 deletions src/examples/java/ca/atlasengine/pathfinding/examples/Demos.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
package ca.atlasengine.pathfinding.examples;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import ca.atlasengine.pathfinding.EntityTargets;
import ca.atlasengine.pathfinding.NavigationSystem;
import ca.atlasengine.pathfinding.adaptive.SharedMeshStatus;
import ca.atlasengine.pathfinding.influence.NavigationInfluence;
import ca.atlasengine.pathfinding.influence.NavigationZoneInfluence;
import ca.atlasengine.pathfinding.metrics.NavigationMetricsSnapshot;
import ca.atlasengine.pathfinding.profile.BuiltinNavigationProfiles;
import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;
import ca.atlasengine.pathfinding.profile.GroundCapabilities;
import ca.atlasengine.pathfinding.metrics.NavigationMetricsSnapshot;
import ca.atlasengine.pathfinding.profile.NavigationMode;
import ca.atlasengine.pathfinding.profile.NavigationModifiers;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.search.NavigationRequest;
import ca.atlasengine.pathfinding.NavigationSystem;
import ca.atlasengine.pathfinding.internal.EntityTargets;
import ca.atlasengine.pathfinding.result.PathResult;
import ca.atlasengine.pathfinding.profile.PlatformJumpCapabilities;
import ca.atlasengine.pathfinding.adaptive.SharedMeshStatus;
import ca.atlasengine.pathfinding.influence.NavigationInfluence;
import ca.atlasengine.pathfinding.influence.NavigationZoneInfluence;
import ca.atlasengine.pathfinding.result.PathResult;
import ca.atlasengine.pathfinding.search.NavigationRequest;
import ca.atlasengine.pathfinding.terrain.BlockManipulationCapabilities;
import ca.atlasengine.pathfinding.terrain.MobTraversalProfile;
import ca.atlasengine.pathfinding.terrain.TerrainClassification;
import ca.atlasengine.pathfinding.terrain.TerrainType;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.EntityType;
Expand Down Expand Up @@ -165,7 +165,8 @@ public static void custom(DemoSession session) {
GroundCapabilities body = GroundCapabilities.STANDARD
.withPlatformJump(PlatformJumpCapabilities.acrossGaps(1));

NavigationProfile profile = NavigationProfile.builder(NavigationMode.GROUND, courier, body).allowBreaching(false).prefersShallowWater(// allowBreaching
NavigationProfile profile = NavigationProfile.builder(NavigationMode.GROUND, courier,
body).prefersShallowWater(// allowBreaching
false).avoidSun(// prefersShallowWater
false).build(); // avoidSun

Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
package ca.atlasengine.pathfinding.examples;

import net.minestom.server.instance.block.Block;
import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;
import ca.atlasengine.pathfinding.EntityNavigationController;
import ca.atlasengine.pathfinding.profile.GroundCapabilities;
import ca.atlasengine.pathfinding.profile.NavigationMode;
import ca.atlasengine.pathfinding.profile.NavigationModifiers;
import ca.atlasengine.pathfinding.NavigationOptions;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.NavigationState;
import ca.atlasengine.pathfinding.NavigationSystem;
import ca.atlasengine.pathfinding.PathDebugRenderer;
import ca.atlasengine.pathfinding.event.NavigationCompletedEvent;
import ca.atlasengine.pathfinding.event.NavigationStuckEvent;
import ca.atlasengine.pathfinding.event.RouteReplanEvent;
import ca.atlasengine.pathfinding.profile.PlatformJumpCapabilities;
import ca.atlasengine.pathfinding.adaptive.SharedMeshNavigation;
import ca.atlasengine.pathfinding.adaptive.SharedMeshOptions;
import ca.atlasengine.pathfinding.adaptive.SharedMeshPolicy;
import ca.atlasengine.pathfinding.adaptive.SharedMeshPursuit;
import ca.atlasengine.pathfinding.adaptive.SharedMeshStatus;
import ca.atlasengine.pathfinding.event.NavigationCompletedEvent;
import ca.atlasengine.pathfinding.event.NavigationStuckEvent;
import ca.atlasengine.pathfinding.event.RouteReplanEvent;
import ca.atlasengine.pathfinding.influence.BlockAvoidanceInfluence;
import ca.atlasengine.pathfinding.influence.EntityFearInfluence;
import ca.atlasengine.pathfinding.influence.EntitySnapshot;
import ca.atlasengine.pathfinding.influence.NavigationArea;
import ca.atlasengine.pathfinding.influence.NavigationInfluence;
import ca.atlasengine.pathfinding.influence.NavigationZoneInfluence;
import ca.atlasengine.pathfinding.influence.ReturnRadiusInfluence;
import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;
import ca.atlasengine.pathfinding.profile.GroundCapabilities;
import ca.atlasengine.pathfinding.profile.NavigationMode;
import ca.atlasengine.pathfinding.profile.NavigationModifiers;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.profile.PlatformJumpCapabilities;
import ca.atlasengine.pathfinding.terrain.BlockManipulationCapabilities;
import ca.atlasengine.pathfinding.terrain.MobTraversalProfile;
import ca.atlasengine.pathfinding.terrain.TerrainClassification;
import ca.atlasengine.pathfinding.terrain.TerrainType;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity;
import net.minestom.server.instance.block.Block;

import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -144,7 +144,8 @@ static EntityNavigationController customMob(
.avoidSun(true)
.build();

return navigation.controller(entity, profile, 0.2);
return navigation.controllerBuilder(entity).profile(profile)
.movementPerTick(0.2).build();
}

/** Wiki, "Platform jumping", "Planned climbing", "Doors". */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package ca.atlasengine.pathfinding.examples;

import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;import ca.atlasengine.pathfinding.profile.PlatformJumpCapabilities;import net.kyori.adventure.text.Component;
import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;
import ca.atlasengine.pathfinding.profile.PlatformJumpCapabilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ca.atlasengine.pathfinding.examples;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import ca.atlasengine.pathfinding.NavigationSystem;
import ca.atlasengine.pathfinding.adaptive.SharedMeshOptions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.Auth;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.GameMode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package ca.atlasengine.pathfinding;

import ca.atlasengine.pathfinding.profile.BuiltinNavigationProfiles;import ca.atlasengine.pathfinding.profile.NavigationProfile;import ca.atlasengine.pathfinding.result.PathNode;import ca.atlasengine.pathfinding.result.PathStatus;import ca.atlasengine.pathfinding.search.NavigationRequest;import ca.atlasengine.pathfinding.adaptive.SharedMeshPolicy;
import ca.atlasengine.pathfinding.internal.adaptive.SharedMeshCoordinator;
import ca.atlasengine.pathfinding.NavigationPlan;
import ca.atlasengine.pathfinding.adaptive.SharedMeshHandle;
import ca.atlasengine.pathfinding.adaptive.SharedMeshPolicy;
import ca.atlasengine.pathfinding.adaptive.SharedMeshSource;
import ca.atlasengine.pathfinding.NavigationPlan;
import ca.atlasengine.pathfinding.internal.adaptive.SharedMeshCoordinator;
import ca.atlasengine.pathfinding.metrics.NavigationMetrics;
import ca.atlasengine.pathfinding.profile.BuiltinNavigationProfiles;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.result.PathNode;
import ca.atlasengine.pathfinding.result.PathStatus;
import ca.atlasengine.pathfinding.search.NavigationRequest;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.EntityType;
Expand Down Expand Up @@ -77,9 +83,13 @@ public void setup() {
.promotionRequests(2).retentionRequests(1)
.maximumNodesPerRegion(1_024).build();
unpromoted = new SharedMeshCoordinator(
(actor, ignored) -> CompletableFuture.completedFuture(plan), cold);
(actor,
ignored) -> CompletableFuture.completedFuture(plan), cold,
new NavigationMetrics());
promoted = new SharedMeshCoordinator(
(actor, ignored) -> CompletableFuture.completedFuture(plan), hot);
(actor,
ignored) -> CompletableFuture.completedFuture(plan), hot,
new NavigationMetrics());
promoted.request("seed-1", "target", 1, 0, request).plan().join();
promoted.request("seed-2", "target", 1, 1, request).plan().join();
if (promoted.stats().promotedRegions() != 1) {
Expand All @@ -95,7 +105,7 @@ private NavigationPlan straightPlan() {
}
return new NavigationPlan(
request.start(), request.target(), box, profile,
PathStatus.FOUND, nodes, nodes.size(), nodes.size() * 8);
PathStatus.FOUND, nodes, nodes.size(), nodes.size() * 8, List.of());
}
}
}
55 changes: 45 additions & 10 deletions src/jmh/java/ca/atlasengine/pathfinding/PathfindingBenchmark.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package ca.atlasengine.pathfinding;

import ca.atlasengine.pathfinding.profile.BuiltinNavigationProfiles;import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;import ca.atlasengine.pathfinding.profile.GroundCapabilities;import ca.atlasengine.pathfinding.profile.NavigationMode;import ca.atlasengine.pathfinding.profile.NavigationProfile;import ca.atlasengine.pathfinding.result.PathNode;import ca.atlasengine.pathfinding.result.PathResult;import ca.atlasengine.pathfinding.search.DiscreteGroundPathfinder;import ca.atlasengine.pathfinding.search.EntityPathfinder;import ca.atlasengine.pathfinding.search.GroundSearchLimits;import ca.atlasengine.pathfinding.search.NavigationRequest;import ca.atlasengine.pathfinding.search.SearchControl;import ca.atlasengine.pathfinding.terrain.MobTraversalProfile;
import ca.atlasengine.pathfinding.profile.BuiltinNavigationProfiles;
import ca.atlasengine.pathfinding.profile.ClimbableCapabilities;
import ca.atlasengine.pathfinding.profile.GroundCapabilities;
import ca.atlasengine.pathfinding.profile.NavigationMode;
import ca.atlasengine.pathfinding.profile.NavigationProfile;
import ca.atlasengine.pathfinding.result.PathNode;
import ca.atlasengine.pathfinding.result.PathResult;
import ca.atlasengine.pathfinding.search.EntityPathfinder;
import ca.atlasengine.pathfinding.search.NavigationRequest;
import ca.atlasengine.pathfinding.search.SearchControl;
import ca.atlasengine.pathfinding.terrain.MobTraversalProfile;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityType;
import net.minestom.server.instance.block.Block;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
Expand Down Expand Up @@ -127,18 +138,37 @@ private static int checksum(PathResult result) {
return checksum;
}

/**
* A default-mob ground search between two exact cells, with the budget
* of eight expansions per node of path length used throughout.
*/
static NavigationRequest groundRequest(World world, Pos start, Pos target,
BoundingBox box, double maxLength) {
NavigationProfile ground = NavigationProfile.builder(
NavigationMode.GROUND, MobTraversalProfile.DEFAULT,
GroundCapabilities.STANDARD)
.pathToTargetsBelowSurface(true)
.build();
return NavigationRequest.builder(world, start, target, box, ground)
.maxPathLength(maxLength)
.maxVisitedMultiplier(8)
.build();
}

@State(Scope.Benchmark)
public static class OpenGround {
final World world = World.flat(-4, 516, -4, 4);
final DiscreteGroundPathfinder engine = new DiscreteGroundPathfinder();
final EntityPathfinder engine = new EntityPathfinder();
final Pos start = new Pos(0.5, 1, 0.5);
final Pos target = new Pos(512.5, 1, 0.5);
final Vec targetVector = target.asVec();
final BoundingBox box = new BoundingBox(0.8, 1.8, 0.8);
int expectedChecksum;

PathResult search() {
return engine.findPath(world, start, target, box, MobTraversalProfile.DEFAULT, GroundSearchLimits.builder().maxPathLength(520).reachRange(0).maxVisitedMultiplier(8).build(), GroundCapabilities.STANDARD, SearchControl.NONE);
return engine.findPath(
groundRequest(world, start, target, box, 520),
SearchControl.NONE);
}

@Setup(Level.Trial)
Expand All @@ -155,7 +185,7 @@ public void setup() {
@State(Scope.Benchmark)
public static class AlternatingWalls {
final World world = World.flat(-4, 260, -24, 24);
final DiscreteGroundPathfinder engine = new DiscreteGroundPathfinder();
final EntityPathfinder engine = new EntityPathfinder();
final Pos start = new Pos(0.5, 1, 0.5);
final Pos targetPosition = new Pos(256.5, 1, 0.5);
final Vec target = targetPosition.asVec();
Expand All @@ -164,7 +194,9 @@ public static class AlternatingWalls {
int expectedChecksum;

PathResult search() {
return engine.findPath(world, start, targetPosition, box, MobTraversalProfile.DEFAULT, GroundSearchLimits.builder().maxPathLength(400).reachRange(0).maxVisitedMultiplier(8).build(), GroundCapabilities.STANDARD, SearchControl.NONE);
return engine.findPath(
groundRequest(world, start, targetPosition, box, 400),
SearchControl.NONE);
}

@Setup(Level.Trial)
Expand Down Expand Up @@ -209,7 +241,8 @@ public void setup() {
}
}
}
NavigationProfile profile = NavigationProfile.builder(NavigationMode.GROUND, MobTraversalProfile.DEFAULT, GroundCapabilities.STANDARD).allowBreaching(false).prefersShallowWater(false).avoidSun(false).build();
NavigationProfile profile = NavigationProfile.builder(NavigationMode.GROUND,
MobTraversalProfile.DEFAULT, GroundCapabilities.STANDARD).build();
request = NavigationRequest.builder(
world, start, targetPosition, box, profile)
.maxPathLength(400)
Expand Down Expand Up @@ -239,7 +272,8 @@ public void setup() {
for (int z = -3; z <= 3; z++) world.set(x, y, z, Block.STONE);
}
}
NavigationProfile profile = NavigationProfile.builder(NavigationMode.FLYING, MobTraversalProfile.DEFAULT, GroundCapabilities.STANDARD).allowBreaching(false).prefersShallowWater(false).avoidSun(false).build();
NavigationProfile profile = NavigationProfile.builder(NavigationMode.FLYING,
MobTraversalProfile.DEFAULT, GroundCapabilities.STANDARD).build();
request = NavigationRequest.builder(world,
new Vec(0.5, 0.5, 0.5), target,
new BoundingBox(0.6, 0.6, 0.6), profile)
Expand Down Expand Up @@ -269,9 +303,10 @@ public ClimbableTower() {
}
NavigationProfile base = BuiltinNavigationProfiles
.forEntityType(
net.minestom.server.entity.EntityType.ZOMBIE);
NavigationProfile climbing = NavigationProfile.builder(base.mode(), base.mobProfile(), base.groundCapabilities().withClimbables(
ClimbableCapabilities.STANDARD)).allowBreaching(base.allowBreaching()).prefersShallowWater(base.prefersShallowWater()).avoidSun(base.avoidSun()).directPathIgnoresFluids(base.directPathIgnoresFluids()).pathToTargetsBelowSurface(base.pathToTargetsBelowSurface()).build();
EntityType.ZOMBIE);
NavigationProfile climbing = base.withGroundCapabilities(
base.groundCapabilities().withClimbables(
ClimbableCapabilities.STANDARD));
request = NavigationRequest.builder(world,
new Pos(0.5, 1, 0.5),
new Pos(1.5, 33, 0.5),
Expand Down
Loading
Loading