Skip to content

Shared Mesh

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

Shared Mesh

Opt-in and off by default. It promotes repeated, compatible traffic into revisioned regional route fields, so a crowd converging on one target does not each pay for a full search.

NavigationSystem navigation = NavigationSystem.builder()
        .sharedMesh(SharedMeshOptions.enabledWith(
                SharedMeshPolicy.builder()
                        .promotionRequests(16)
                        .regionSize(64)
                        .maximumRegions(128)
                        .build()))
        .build();

SharedMeshNavigation mesh = navigation.sharedMesh();
mesh.tick(tick);        // once per world tick
mesh.disable();         // out of service without touching a call site

mesh.tick(tick) takes a tick id, not a timestamp. It is compared against targetIdleTicks and regionIdleTicks, and a pursuit swallows a repeated id so a mob is never walked twice. Feeding it System.currentTimeMillis() would expire retained routes about fifty times early.

Pursuits

A pursuit owns its follower and ticks it — the opposite contract to a plain controller.

SharedMeshPursuit pursuit = mesh.pursue(hostileMob, player, worldRevision, tick);

pursuit.tick(worldRevision, tick);      // instance tick thread only
NavigationState state = pursuit.state();
pursuit.close();                        // death and removal are also detected
You hold You call once per tick Who ticks the follower
EntityNavigationController controller.tick() you
SharedMeshPursuit pursuit.tick(worldRevision, tick) the pursuit

A pursuit never hands out its follower; read state(), nodes(), generation() and spliceCounts() instead. Driving one from two places is safe: one tick id advances it once.

Read this before enabling it

Enabled wrongly it costs more than it saves.

  • Regions partition on the whole request — world, regional start cell, bounding box, profile, ordered influence snapshot, sea level, all four search limits and build heights. A diverse population can exhaust the default maximumRegions = 128, after which every further request takes UNTRACKED_FALLBACK: ordinary A* plus bookkeeping, strictly worse than leaving the mesh off. Watch status().regionsExhausted().
  • A destination merges to its cell; a source never merges. Reuse happens when a follower's own position repeats. Measured on the seeded sweep, a stationary pursuer answered 61.7% of requests from a shared field, a moving one 0%.
  • Promotion is a visible hitch — about 8.1 ms inline against a 1.2 ms baseline for the same search.
  • The steady-state win is about twelvefold, not orders of magnitude: roughly 0.1 to 0.2 ms against 1.2 ms end to end.

Enable it for crowds converging on shared objectives from repeated positions: sieges, spawn corridors, chokepoints, guards holding a post. Leave it off for sparse populations, varied bounding boxes and profiles, or mobs in constant open transit.

Planning a prebuilt request

SharedMeshHandle handle = mesh.plan(SharedMeshRequest.builder(request)
        .actor(mob.getUuid())
        .target(target.getUuid())
        .worldRevision(worldRevision)
        .currentTick(tick)
        .build());

NavigationStrategy defaults to PREFER_SHARED, which consults the mesh and falls back to ordinary A*; INDIVIDUAL_ONLY never consults it. Neither relaxes the parity gate: a shared plan is served only from a promoted region holding a certified ordinary-A* plan for that exact source and destination class. There is no force-shared mode. handle.source() reports what answered.

mesh.status() bundles the switch state, the policy, SharedMeshRetention and this mesh's own request counts. That is what to alarm on when it is enabled.

Building a mesh by hand

SharedRouteMesh is the low-level primitive: directed edges of complete NavigationPlan segments, directed because climbing, falling, jumping, doors and costs are not symmetric.

SharedRouteMesh<String> mesh = SharedRouteMesh.<String>builder(worldRevision)
        .route("spawn-west", "junction", westToJunction, 12.5)
        .route("junction", "arena", junctionToArena, 20.0)
        .build();

NavigationPlan plan = mesh.routesTo("arena", worldRevision)
        .routeFrom("spawn-west")
        .orElseThrow()
        .plan();

A target field runs reverse Dijkstra once and stores one next edge per reachable node. A mesh key must include the world and its revision, the profile and cost policy, entity width, height and depth, movement capabilities including jump and fall limits, and the allowed areas. Never share a mesh across incompatible bounding boxes or profiles.

Shared routes do not solve crowd collision. Add a local steering or reservation layer for velocity and short-term lanes.

Clone this wiki locally