Skip to main content

Sentinel Navigation

Overview

The SentinelNavClient is a third-party navigation library that provides shared pathfinding and movement for consumer plugins through a single global client instance (_G.SentinelNavClient.client).

It handles pathfinding integration with SentinelNavServer, runtime movement behavior (HSM + Behavior Tree), obstacle probing and avoidance-zone memory, and in-game settings UI and visualization.

Third-Party Library

The original developer of the navigation system is not part of the internal PS team. This is a third-party plugin and the API documented here could be outdated. This documentation is based on the API as of February 2026 (v0.0.5).

note

Requires SentinelNavServer running and reachable. Use client:is_server_available() to check before issuing navigation commands.


Quick Start

if not (_G.SentinelNavClient and _G.SentinelNavClient.client) then
core.log_error("SentinelNavClient not loaded")
return
end

local client = _G.SentinelNavClient.client

client:move_to({ x = -8900, y = 560, z = 94 }, function(ok, reason)
if ok then
core.log("Arrived")
else
core.log_error("Navigation failed: " .. tostring(reason))
end
end)
Consumer Rules
  • Do not call client:update() from consumer plugins — SentinelNavClient drives updates internally.
  • Do not own navigation tuning in consumer plugins — SentinelNavClient UI owns config.

Global Table

_G.SentinelNavClient

FieldTypeDescription
clientsentinel_nav_client|nilLive getter for the shared singleton Client
create(config?)functionReturns the shared Client. config is accepted but ignored
uitableUI orchestrator module
JSONtableJSON utility module
HelperstableGeneral helper utility module
VERSIONstringCurrent plugin version

Movement Commands

client:move_to(target, callback?, opts?)

Syntax
client:move_to(target, callback?, opts?)

Parameters

  • target (table) — { x, y, z } destination position.
  • callback (function?) — function(success: boolean, reason: string?) called on completion.
  • opts (table?) — Optional overrides copied to blackboard as opts.*.
Description

Pathfinds to the target and starts navigation. Defers the move while casting (enters navigating.deferred). Otherwise transitions into navigating.awaiting_path and the Behavior Tree requests a path.

Example Usage

client:move_to({ x = -8900, y = 560, z = 94 }, function(ok, reason)
if ok then
core.log("Arrived at destination")
else
core.log_error("Move failed: " .. tostring(reason))
end
end)

client:move_direct(target, callback?)

Syntax
client:move_direct(target, callback?)

Parameters

  • target (table) — { x, y, z } destination position.
  • callback (function?) — function(success: boolean, reason: string?) called on completion.
Description

Follows a direct single-waypoint path to the target without pathfinding. Useful when the path is known to be clear.


client:follow_path(waypoints, callback?)

Syntax
client:follow_path(waypoints, callback?)

Parameters

  • waypoints (table[]) — Array of { x, y, z } positions to follow in order.
  • callback (function?) — function(success: boolean, reason: string?) called on completion.
Description

Follows a precomputed list of waypoints. Commonly used to execute a route returned by plan_route.


client:plan_route(nodes, callback?, opts?)

Syntax
client:plan_route(nodes, callback?, opts?)

Parameters

  • nodes (table[]) — Array of { x, y, z } positions to visit.
  • callback (function?) — function(success: boolean, data: table?) called with route data.
  • opts (table?) — Optional route options (e.g. { return_to_start = false }).
Returns (via callback)

On success, data contains:

FieldTypeDescription
waypointsvec3[]Ordered waypoints for the full route
visit_orderinteger[]1-based visitation order
leg_boundariesinteger[]Waypoint indices where legs start
leg_distancesnumber[]Distance of each leg
total_distancenumberTotal route distance

On failure, data may be nil or { error = string }.

Description

Plans a TSP (Traveling Salesman Problem) route through the given nodes. This is a planning-only call — it does not execute the route. To execute the route, call client:follow_path(data.waypoints, ...) with the returned waypoints.

Example Usage

local nodes = {
{ x = -9100, y = 400, z = 93 },
{ x = -9200, y = 500, z = 91 },
{ x = -8900, y = 600, z = 95 },
}

client:plan_route(nodes, function(ok, data)
if not ok then
core.log_error("Route plan failed: " .. tostring(data and data.error))
return
end

client:follow_path(data.waypoints, function(success, reason)
if success then
core.log("Route execution complete")
else
core.log_error("Route execution failed: " .. tostring(reason))
end
end)
end, {
return_to_start = false,
})

client:validate_destination(target, callback)

Syntax
client:validate_destination(target, callback)

Parameters

  • target (table) — { x, y, z } destination position.
  • callback (function) — function(reachable: boolean, reason: string?, distance: number?).
Description

Reachability probe without starting movement. Use this to check if a destination is reachable before committing to navigation.

Example Usage

client:validate_destination(target, function(reachable, reason, distance)
if not reachable then
core.log_warning("Target unreachable: " .. tostring(reason))
return
end

client:move_to(target, function(ok, fail_reason)
if ok then
core.log("Arrived (" .. string.format("%.1f", distance or 0) .. " yd)")
else
core.log_error("Move failed: " .. tostring(fail_reason))
end
end)
end)

client:replan(reason?)

Syntax
client:replan(reason?)

Parameters

  • reason (string?) — Optional reason for the replan (for logging).
Description

Re-requests the current path to the current destination by clearing path state and returning to awaiting_path.


client:stop()

Syntax
client:stop()
Description

Stops all movement and resets the active navigation state.


State and Progress

client:get_state()

Syntax
client:get_state() -> string
Returns
  • string — One of: "idle", "navigating", "arrived", "failed".

client:get_full_state()

Syntax
client:get_full_state() -> string
Returns
  • string — Dot-joined hierarchical state (e.g. "navigating.recovering.strafing").

client:is_moving()

Syntax
client:is_moving() -> boolean
Returns
  • booleantrue only while the top-level state is navigating.

client:get_destination()

Syntax
client:get_destination() -> vec3|nil
Returns
  • vec3|nil — The current navigation destination, or nil if idle.

client:get_current_path()

Syntax
client:get_current_path() -> vec3[]|nil
Returns
  • vec3[]|nil — The current list of waypoints being followed, or nil.

client:get_path_index()

Syntax
client:get_path_index() -> number
Returns
  • number — The index of the waypoint currently being approached.

client:get_progress()

Syntax
client:get_progress() -> sentinel_nav_progress
Returns
FieldTypeDescription
percentnumberProgress from 0 to 1
waypoints_remainingnumberWaypoints left
total_waypointsnumberTotal waypoints in path
current_indexnumberCurrent waypoint index

Example Usage

if client:is_moving() then
local full = client:get_full_state()
local p = client:get_progress()
core.log(string.format(
"[%s] %.0f%% (%d/%d)",
full,
(p.percent or 0) * 100,
p.current_index or 1,
p.total_waypoints or 0
))
end

client:get_corridor_widths()

Syntax
client:get_corridor_widths() -> number[]|nil
Returns
  • number[]|nil — Corridor widths per waypoint, or nil if not available.

Server Queries

client:is_server_available()

Syntax
client:is_server_available() -> boolean
Returns
  • booleantrue when the server connection flag is up.
Description

Reflects the blackboard connection flag maintained by NavigationService. Set true after successful responses, marked disconnected after repeated failures.


client:health_check(callback)

Syntax
client:health_check(callback)

Parameters

  • callback (function) — function(ok: boolean) called with server health result.

Example Usage

if not client:is_server_available() then
client:health_check(function(ok)
if ok then
core.log("SentinelNavServer reachable")
else
core.log_warning("SentinelNavServer unavailable")
end
end)
end

client:get_height(pos, callback)

Syntax
client:get_height(pos, callback)

Parameters

  • pos (table) — { x, y, z } position.
  • callback (function) — Callback with navmesh height at position.

client:get_player_height(callback)

Syntax
client:get_player_height(callback)

Parameters

  • callback (function) — Callback with navmesh height at current player position.

client:get_all_heights(pos, callback, opts?)

Syntax
client:get_all_heights(pos, callback, opts?)

Parameters

  • pos (table) — { x, y, z } position.
  • callback (function) — Callback with multi-layer heights at the XY position.
  • opts (table?) — Optional options.

client:get_player_all_heights(callback, opts?)

Syntax
client:get_player_all_heights(callback, opts?)

Parameters

  • callback (function) — Callback with multi-layer heights at the player position.
  • opts (table?) — Optional options.

Events

Legacy Events (client:on / client:off)

client:on("arrived", function()
core.log("Arrived at destination")
end)

client:on("failed", function()
core.log_error("Navigation failed")
end)

client:on("state_change", function(data)
core.log("State: " .. tostring(data.from) .. " -> " .. tostring(data.to))
end)

client:on("stuck", function()
core.log_warning("Stuck detected")
end)
EventPayload
"state_change"{ from: string, to: string }
"arrived"
"stuck"— (mapped from navigating.recovering)
"failed"

EventBus Events

For more granular control, use the EventBus:

local bus = client:get_event_bus()

bus:on("nav.state_changed", function(data, event_name)
core.log(event_name .. ": " .. tostring(data.from) .. " -> " .. tostring(data.to))
end, { owner = self, priority = 50 })

bus:on_pattern("nav.server_*", function(data, event_name)
core.log("Server event: " .. event_name)
end, { owner = self })

-- Cleanup:
bus:off_owner(self)

Available Events:

EventDescription
nav.state_changedState machine transition
nav.arrivedDestination reached
nav.failedNavigation failed
nav.stuck_detectedStuck condition detected
nav.stuck_recoveredRecovered from stuck
nav.deviation_detectedPath deviation detected
nav.repath_startedRepath in progress
nav.repath_completedRepath finished
nav.obstacle_detectedObstacle encountered
nav.path_requestedPath request sent to server
nav.path_receivedPath response received
nav.path_failedPath request failed
nav.waypoint_reachedA waypoint was reached
nav.server_connectedServer connection established
nav.server_retryServer retry attempt
nav.server_disconnectedServer connection lost
nav.server_errorServer error occurred

EventBus Methods:

MethodDescription
bus:on(event, callback, opts?)Subscribe to event
bus:once(event, callback, opts?)Subscribe once
bus:on_pattern(pattern, callback, opts?)Subscribe to pattern (e.g. "nav.server_*")
bus:off(id)Unsubscribe by ID
bus:off_owner(owner)Unsubscribe all by owner
bus:pause()Pause event delivery
bus:resume()Resume event delivery

State Model

Top-Level States

StateDescription
idleNo active navigation
navigatingActively navigating
arrivedDestination reached
failedNavigation failed
SubstateDescription
awaiting_pathWaiting for path from server
following_pathFollowing the active path
recoveringAttempting stuck recovery
repathingRe-requesting the path
deferredMove deferred (e.g. while casting)

Recovery Sub-Substates

Sub-SubstateDescription
jumpingAttempting to jump over obstacle
probingProbing for alternative path
strafingStrafing sideways
backtrackingMoving backwards

Failure Reasons

ReasonDescription
unreachableDestination cannot be reached
server_timeoutServer did not respond
max_stuck_exceededToo many stuck recovery attempts
max_repath_exceededToo many repath attempts

Advanced APIs

Use With Care

These are lower-level services exposed on the client. They are more likely to change than the high-level Client API.

Accessible as fields on the client:

  • client.nav_client — NavigationService (core pathing, spatial queries, tactical)
  • client.movement — MovementService (waypoint following, strafe, speed)
  • client.obstacle — ObstacleService (probing, avoidance zones)
MethodDescription
find_path(start, dest, callback, opts?)Core A* pathfinding
find_route_tsp(nodes, callback, opts?)TSP route planning
find_route_multi(stops, callback, opts?)Multi-stop route
check_path(pos, waypoints, callback, opts?)Validate existing path
find_path_corridor(start, dest, callback, opts?)Path with corridor widths
find_path_avoid(start, dest, zones, callback, opts?)Path avoiding zones
raycast(start, dest, callback, opts?)Navmesh raycast
get_height(pos, callback, opts?)Navmesh height query
get_all_heights(pos, callback, opts?)Multi-layer height query
random_point(callback, opts?)Random reachable point
flee(player_pos, threats, callback, opts?)Flee from threats
kite(player_pos, target_pos, callback, opts?)Kite a target
health_check(callback)Server health ping
is_available()Server connection status
NavigationService.is_indoor()Indoor detection (static)
NavigationService.get_continent_id()Current continent ID (static)

MovementService (client.movement)

MethodDescription
navigate(waypoints)Start following waypoints
process()Tick movement processing
stop()Stop movement
get_current_index()Current waypoint index
get_remaining_waypoints()Remaining waypoint count
is_moving()Is movement active
strafe(direction)Strafe in a direction
apply_dynamic_speed(blackboard?)Apply dynamic speed adjustments

ObstacleService (client.obstacle)

MethodDescription
probe_forward(player_pos, target_pos)Probe ahead for obstacles
probe_segment(pos_a, pos_b)Probe a line segment
probe_path_ahead(waypoints, max_segments?)Probe multiple segments ahead
add_zone(pos, radius?)Add avoidance zone
remove_zone(index)Remove avoidance zone
get_avoidance_zones()Get all avoidance zones
get_zone_count()Number of active zones
clear()Clear all zones