Navigators¶
A navigator renders a trip. The built-in trail navigator draws particles along the path.
Players select a navigator with -navigator <id>; plugins select one when starting a trip.
Interface¶
public interface Navigator<L> {
void start(); // the trip begins
void tick(); // every server tick: render, advance
void update(Path<L, MinecraftStepPayload> p); // a re-search replaced the path
void stop(); // arrival, cancellation, or logout
boolean isComplete(); // has the player arrived?
double remainingSeconds(); // for /cobblestone trips
// Optional:
default boolean consumeRecalcRequest(); // "I've lost them, re-search"
default Optional<L> consumeGuideRequest(); // "give me a short path back to the trail"
default void setGuidePath(Path<L, MinecraftStepPayload> guide);
}
tick() runs every server tick on the player's scheduler (a region task on Folia). It must not
block.
The trip ends when isComplete() returns true. Arrival is determined by the navigator; the
built-in trail uses a two-block radius.
Example¶
An action-bar compass that displays a bearing:
final class CompassNavigator implements Navigator<Location> {
private final Player player;
private Path<Location, MinecraftStepPayload> path;
private int step;
CompassNavigator(Player player, Path<Location, MinecraftStepPayload> path) {
this.player = player;
this.path = path;
}
@Override public void start() { step = 0; }
@Override public void tick() {
if (isComplete() || !player.isOnline()) {
return;
}
Location target = path.steps().get(step).position();
if (player.getLocation().distanceSquared(target) < 4) {
step++;
return;
}
player.sendActionBar(Component.text(bearing(player.getLocation(), target)));
}
@Override public void update(Path<Location, MinecraftStepPayload> newPath) {
this.path = newPath;
this.step = 0;
}
@Override public void stop() {
player.sendActionBar(Component.empty());
}
@Override public boolean isComplete() {
return step >= path.steps().size();
}
@Override public double remainingSeconds() {
double total = 0;
for (int i = step; i < path.steps().size(); i++) {
total += path.steps().get(i).time();
}
return total;
}
}
final class CompassNavigator implements Navigator<ServerLocation> {
private final ServerPlayer player;
private Path<ServerLocation, MinecraftStepPayload> path;
private int step;
CompassNavigator(ServerPlayer player, Path<ServerLocation, MinecraftStepPayload> path) {
this.player = player;
this.path = path;
}
@Override public void start() { step = 0; }
@Override public void tick() {
if (isComplete() || !player.isOnline()) {
return;
}
ServerLocation target = path.steps().get(step).position();
if (player.position().distanceSquared(target.position()) < 4) {
step++;
return;
}
player.sendActionBar(Component.text(bearing(player.serverLocation(), target)));
}
@Override public void update(Path<ServerLocation, MinecraftStepPayload> newPath) {
this.path = newPath;
this.step = 0;
}
@Override public void stop() {
player.sendActionBar(Component.empty());
}
@Override public boolean isComplete() {
return step >= path.steps().size();
}
@Override public double remainingSeconds() {
double total = 0;
for (int i = step; i < path.steps().size(); i++) {
total += path.steps().get(i).time();
}
return total;
}
}
Registration¶
Ids are lower-case. The first registration of an id takes precedence. The navigator is gated by
cobblestone.navigator.<id> (default allow) and removed when the owner disables.
Settings¶
NavigatorSettings carries per-trip overrides. Declare typed keys for the settings your navigator
supports:
public final class CompassSettings {
public static final String NAVIGATOR_ID = "compass";
public static final NavigatorSettingKey<Boolean> SHOW_DISTANCE =
new NavigatorSettingKey<>("compass.show_distance");
public static NavigatorSettings showingDistance(boolean show) {
return NavigatorSettings.builder(NAVIGATOR_ID).set(SHOW_DISTANCE, show).build();
}
}
Read them in the factory, falling back to configuration when unset:
Reading the path¶
A Path is an origin and an ordered list of Steps. Each step has a position, cost and time in
seconds, and a MinecraftStepPayload:
for (Step<Location, MinecraftStepPayload> step : path.steps()) {
MinecraftStepType type = step.payload().stepType(); // WALK, SWIM, FALL, BOAT, TELEPORT…
MinecraftInstruction instruction = step.payload().instruction();
}
path.duration() is the estimated travel time; path.cost() is the value minimized by the search.
Action steps¶
OPEN_DOOR, PLACE_BOAT, MOUNT_HORSE, and TELEPORT require player action.
MinecraftStepType#isAction() identifies them. A step may also carry an instruction:
if (step.payload().instruction()
instanceof MinecraftInstruction.CommandInstruction(String command)) {
// e.g. "/warp market" — make it clickable rather than making them type it.
player.sendMessage(Component.text("Run " + command)
.clickEvent(ClickEvent.runCommand(command)));
}
Navigators should notify the player at action steps.
Straying¶
Two optional hooks handle players who leave the route:
consumeRecalcRequest(): Returntrueto re-search from the player's position. The new path is passed toupdate.consumeGuideRequest(): Return a target to compute a short path from the player to it. The result is passed tosetGuidePath.
Both default to no request.