Skip to content

Reacting to Navigation

Alexander Parent edited this page Jul 31, 2026 · 1 revision

Reacting to Navigation

navigation.eventNode() is a normal Minestom EventNode. One listener serves every mob the system owns, so you do not need a reference to each controller.

navigation.eventNode()
        .addListener(NavigationCompletedEvent.class, e -> onArrived(e.getEntity()))
        .addListener(NavigationStuckEvent.class, e -> unwedge(e.getEntity()))
        .addListener(TargetUnreachableEvent.class, e -> chooseAnotherGoal(e.getEntity()));

It filters and composes like any other node:

navigation.eventNode().addChild(
        EventNode.type("guards", EventFilter.ENTITY, (e, entity) -> isGuard(entity)));

Every event

Event Fired when
NavigationCompletedEvent arrived
NavigationStuckEvent cannot walk its route
TargetUnreachableEvent no further search reaches the target
NavigationFailedEvent the navigation failed
NavigationCancelledEvent cancelled, including by close()
PathComputedEvent a route was adopted; status(), stop(), nodes()
PathShedEvent shed by backpressure; retryTick()
RouteReplanEvent a replan was decided; see reason() below

Why did it replan?

navigation.eventNode().addListener(RouteReplanEvent.class,
        e -> log(e.getEntity(), e.reason()));
Reason Meaning
NEW_TARGET someone called moveTo
BLOCK_CHANGE a block changed near the route still to walk
PARTIAL_CONTINUATION the route ran out short and may be extended
SEARCH_STALLED a pending search looked lost and was replaced
SHED_RETRY a shed request's backoff elapsed
REQUESTED recomputePath() was called

This is the fastest way to answer "why does this mob keep re-pathing?".

The rules

  • Events are called on the thread that ticked the controller, so a listener may touch the entity directly. Searches finish on workers, but a result is announced when the controller adopts it.
  • Outcomes fire once per transition, not once per tick they persist.
  • Nothing is allocated until the node has a listener for that type, and the node itself is created the first time you ask for it.
  • Every event carries controller(); entity events carry getEntity(), so EventFilter.ENTITY works.

Clone this wiki locally