Skip to content

build(deps)!: move Maul Nav to 1.2.0 - #222

Merged
siracozmen01 merged 1 commit into
mainfrom
deps-maul-nav-1.2.0
Nov 5, 2026
Merged

siracozmen01 merged 1 commit into
mainfrom
deps-maul-nav-1.2.0

Conversation

@siracozmen01

Copy link
Copy Markdown
Contributor

Problem

The engine pins Maul Nav 0.2.0. Maul Nav 1.2.0 is released and verified. Between them lie eight breaking 0.x releases (0.3.0 to 0.8.0, and the 0.8.x fixes), then 1.0.0, which settles the API.

Change

  • third_party/maul-nav is Maul Nav 1.2.0. It was vendored with tools/update_maul.sh maul-nav maul-nav-1.2.0.tar.gz, which checked the archive against the release's SHA256SUMS. The archive has one attestation on GitHub, and its tag is the commit docs/operations/dependencies.md now gives.
  • modules/navigation/src/navmesh.cpp and avoidance.cpp, the engine's only Maul Nav callers, take 1.2.0's API. rawframe/navigation's public API does not change, and nothing above it changes.
  • modules/navigation/tests/navmesh_test.cpp: the navmesh fingerprint golden moves from 0x530d3bbdede27404 to 0x4a35ccb805b9f2fb. The test's path is unchanged: four points, the same corners, 31.895 m. Only the tile bytes it hashes moved.

Every breaking release, against the engine's calls

The engine calls, before and after:

0.2.0 1.2.0
mnavDefaultBakeDef, mnavCreateNavmesh(&bakeDef, ...) mnavDefaultNavmeshDef, its bake the bake def, mnavCreateNavmesh(&navmeshDef, ...)
mnavCreateBaker(&bakeDef, ...), mnavCreateQuery, mnavCreateAvoidance unchanged names
mnavBakeTile, mnavCopyBakedTile mnavBaker_BakeTile, mnavBaker_CopyTile
mnavStageTile, mnavStageTileRemoval, mnavCommit mnavNavmesh_StageTile, mnavNavmesh_StageTileRemoval, mnavNavmesh_Commit
mnavFindNearest(navmesh, filter, point, halfExtents, out) mnavNavmesh_FindNearest(navmesh, point, halfExtents, filter, out)
mnavFindPath(query, navmesh, filter, startPolygon, start, endPolygon, end, out) mnavQuery_FindPath(query, navmesh, startPolygon, start, endPolygon, end, filter, out)
mnavAvoid mnavAvoidance_Avoid
mnavHash64(seed, data, int32_t) mnavHash64(seed, data, int64_t)

0.3.0

  • Names and argument orders: the table above. The filter, which the engine passes as NULL, moved after the inputs. No result changes from the move itself.
  • Velocities and directions are binary32. mnavAgent.velocity and preferred and the velocities written are mnavVec2. The engine rounds each Ground velocity to binary32 once on the way in, and widens each one written back.
  • Result moves: avoidance computes in binary64 but rounds each velocity it gives to binary32, never past the agent's maximum speed. A velocity held to the maximum speed no longer passes it by an ulp. An agent faster than its maximum speed and held back by nothing goes straight along its preferred direction, where it aimed about 0.57 degrees to the right. A Kest game whose rawframe.navigation.agent entities avoid each other in the rawframe.navigation.avoid system, as tycoon's customers do, sees them move by slightly different amounts each step.
  • Outputs are zeroed on failure. The engine reads mnavNavmesh_FindNearest's and mnavQuery_FindPath's outputs only on success, and treats a failed nearest as none found, as before.
  • mnavWall, tile and cell ranges, and mnavBaker_CopyTile refusing an empty baker with mnav_errorState: none are reached. The engine copies a tile only after a successful bake.
  • Fixed: a zero-area triangle when a tile-border vertex is removed. No known bake input reaches it.

0.4.0

  • A navmesh is made from an mnavNavmeshDef. The engine's tier (dynamic) and limits.tiles move there from the bake def, and limits.memoryBytes is set on both the navmesh's and the bake def's, as the shared def gave both before. Each of the navmesh and its baker keeps its own budget, as it did. The engine also reports the refused bake setting a navmesh def names.
  • A bake report names a refused input by kind and index. The engine reads only stage.
  • Hierarchical search, the grid flow field, and busy refusals from host callbacks: none are reached. The engine gives no allocator and no clearance function.

0.5.0

  • Arrays zeroed over capacity on failure: mnavBaker_CopyTile is the engine's only such call, and it copies into a buffer of exactly the size it just measured.
  • Debug calls are not used. mnavBakeSetting's values are renamed, but the engine reports only the number.
  • mnavHash64 takes an int64_t count. Its result is unchanged for every count that fits in an int32_t, which every tile does.

0.6.0

  • Every enumeration typedef is int32_t, and report counters are named ...Count: the engine reads report.polygonCount where it read polygons.
  • Result moves: MNAV_BAKE_GENERATOR 4 changes every tile's bytes and fingerprint. The engine's bake def takes Maul Nav's default maxEdgeLength (12 m) and detailSampleDistance (1.5 m), both far above the changed range at the engine's 0.2 m cells (below one cell, and below a sixteenth of one), so only the generator byte moves for that reason.
  • Fixed: a commit or destroy no longer frees a tile while its ids are live. The engine gives no free hook.

0.7.0

  • Result moves: a walkable region one cell thin is kept instead of dropped, so a tile side no longer faces nothing where its neighbour kept those cells. MNAV_BAKE_GENERATOR 5 moves every tile's bytes again, and a game's navmesh gets a polygon where such a strip had none, so a path may now cross it.
  • Debug buffer counts and link generation's short-buffer answers: not used.

0.8.0

  • Result moves: an edge takes up to 41 detail samples instead of 20. MNAV_TILE_FORMAT is 4 and MNAV_BAKE_GENERATOR is 6. Polygons and links do not change, so paths cross the same polygons. Heights read from the detail surface move along edges that now carry more samples, which reaches the height of a path's points over uneven ground. Tiles grow by about 4%.
  • The engine keeps no baked tile across runs. A game's server bakes its navmesh from the World's geometry as it starts and as that geometry changes, and keeps tiles in memory only, so no stored tile has to be baked again. The fingerprint a server reports in its navigation_summary record moves, and no test reads its value.
  • Flight tiles: not used.

0.8.1 to 0.8.6

Each fix concerns a call the engine does not make, or input it never gives. None is reached:

  • 0.8.1: mnavQuery_ReplanCorridor.
  • 0.8.2: mnavGridFlow_Update.
  • 0.8.3: mnavSteer.
  • 0.8.4: a query def with half an allocator. The engine passes the default def.
  • 0.8.5: mnavSetCorridor and corridors.
  • 0.8.6: mnavDebugPath.

1.0.0 to 1.2.0

  • 1.0.0: the API settles, and nothing changes.
  • 1.0.1: mnavQuery_FindShortestPath's corridor. The engine searches with mnavQuery_FindPath, so it is not reached.
  • 1.1.0:
    • mnavPath gains query and generation. The engine reads end, points and pointCount.
    • A query context holds 76 bytes more per node: about 623 KB more for each navmesh, at the default 8192 nodes.
    • The wall queries, mnavSetCorridor, mnavDebugPath and mnavSteer are not used.
  • 1.1.1: MNAV_NODISCARD under GCC 14 in C23. It changes what Maul Nav's own C is warned about, not results.
  • 1.1.2: mnavQuery_FindShortestPath far from the origin. Not reached.
  • 1.1.3:
    • Result moves: a navmesh query's point or end more than 1e100 m from the navmesh's origin is refused with mnav_errorLimit, where it gave a wrong answer.
    • For the engine, Navmesh::nearest gives none for such a point, and Navmesh::path an empty path, as for a point off the navmesh. A Kest program's Navigation.next toward a point past 1e100 m now finds no way instead of a wrong one.
  • 1.2.0: mnavQuery_GenerateLinks on a navmesh baked with agent radius 0. Not used.

No wire, save, checkpoint or cooked format changes: no navmesh tile is saved or cooked.

Verification

  • navigation's tests: with the old golden, APathGoesAroundAWallAndStraightOnceItIsGone failed only at the fingerprint, with the path unchanged: 4 points, the same corners, 31.895 m. The new value holds in every tree below, so the bytes are the same on Clang, GCC 14 and wasm32.
  • tools/check.sh full passed at four jobs through the heavy-job wrapper, in 1596 s:
    • clang-thread: 208 of 208.
    • clang-sanitize: 253 of 253.
    • gcc-debug: 208 of 208.
    • clang-development: 208 of 208 unit tests and 19 of 19 integration tests. Those include tycoon's bots, whose server runs rawframe.navigation.avoid and reports its navigation_summary.
    • wasm-development: 62 of 62.
    • The web page and its plays passed in the browser.
  • GCC 14: Maul Nav was cleaned and rebuilt alone in gcc-debug: 76 C objects, no warning.
  • No test pins an avoidance velocity or a reported fingerprint other than the one above, so no other golden moves.

BREAKING CHANGE: the engine is built on Maul Nav 1.2.0. A navmesh's tile bytes and fingerprint change: tiles bake under generator 6 and tile format 4. A walkable strip one cell thin now gets a polygon, and heights along long edges are sampled more finely. Avoidance velocities are rounded to binary32 and never pass an agent's maximum speed, so a game's agents that avoid each other move by slightly different amounts. A navigation query past 1e100 m from the navmesh's origin finds nothing.

The engine's navigation module takes Maul Nav 1.2.0's names, argument
orders and defs: a navmesh made from its own def that holds the bake
def, and avoidance velocities in binary32. Tiles bake under a later
generator and tile format, so the navmesh fingerprint moves.

BREAKING CHANGE: a navmesh's tile bytes and fingerprint change, and
avoidance velocities are rounded to binary32, so a game's agents that
avoid each other move by different amounts.

@siracozmen01 siracozmen01 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at c285aae: ready, with one point not verified. No findings. Read, not run. The vendored tree's identity with the archive was checked by the manager.

The engine's calls, against the vendored 1.2.0 headers:

  • mnavAvoidance_Avoid(avoidance, agents, count, obstacles, obstacleCount, step, mnavVec2* velocitiesOut) (avoidance.h). mnavAgent.velocity and preferred are mnavVec2. velocityOf rounds each part once, and the velocities vector is now mnavVec2, widened on the way out.
  • mnavNavmesh_FindNearest(navmesh, point, halfExtents, filter, out) and mnavQuery_FindPath(query, navmesh, startPolygon, start, endPolygon, end, filter, out) match the new argument orders, with the filter NULL in both.
  • mnavNavmeshDef has bake, tier and limits. mnavNavmeshDefResult has setting and bakeSetting, and the latter is mnav_bakeSettingNone unless the setting is mnav_navmeshSettingBake, so the new context is harmless otherwise.
  • The memory budget is set on both the navmesh's limits and the bake def's limits. The bake def still goes to mnavCreateBaker, and the navmesh def's comment says the allocator, limits and clock in bake are the bakers'.
  • mnavBakeReport.polygonCount, mnavBaker_BakeTile and CopyTile, mnavNavmesh_StageTile, StageTileRemoval and Commit are pure renames.
  • mnavHash64 takes int64_t, and every count passed fits.

The golden move is explained by Maul Nav's changelog at v1.2.0:

  • 0.2.0 baked under tile format 3 and generator 3.
  • 0.6.0 sets MNAV_BAKE_GENERATOR 4, 0.7.0 sets 5, and 0.8.0 sets 6 with MNAV_TILE_FORMAT 4. Each is entered as changing every tile's bytes and fingerprint. The vendored base.h/bake.h define format 4 and generator 6.
  • 0.8.0 leaves polygons and links unchanged, and 0.7.0 only adds polygons on one-cell strips the test scene does not have. That fits a path that stays four points and 31.895 m.
  • 1.0.0 to 1.2.0 change no tile bytes (1.0.0 says so outright, and 1.0.1 to 1.2.0 are query, corridor, wall and link fixes).

The body's per-release account matches the changelog's breaking entries. The 1.1.3 claim holds in the code: Navmesh::path returns an empty Path when either nearest fails (navmesh.cpp:263), so mnavQuery_FindPath only ever sees points FindNearest snapped onto the navmesh, never one past 1e100 m.

Not verified: that no other doc or source outside third_party/ still names Maul Nav 0.2.0 or an old call (mnavFindPath, mnavFindNearest, mnavAvoid(, mnavBakeTile, mnavCopyBakedTile, mnavStageTile, mnavCommit, mnavDefaultBakeDef used for a navmesh). dependencies.md is updated. I cannot search the tree, and a build would catch a stale call but not a stale doc.

The ! and the BREAKING CHANGE footer fit a dependency move that changes results. No line of the body starts a commit header.

@siracozmen01
siracozmen01 merged commit 9c547e2 into main Nov 5, 2026
10 checks passed
@siracozmen01
siracozmen01 deleted the deps-maul-nav-1.2.0 branch November 5, 2026 11:08
@rawframe-release rawframe-release Bot mentioned this pull request Nov 5, 2026
siracozmen01 pushed a commit that referenced this pull request Nov 5, 2026
🤖 I have created a release *beep* *boop*
---


## [0.7.0](v0.6.1...v0.7.0) (2026-11-05)

This release moves the engine to Maul Nav 1.2.0, Maul UI 1.3.3 and Maul Unicode 1.1.2. A navmesh's tiles bake to new bytes, and agents that avoid each other move by slightly different amounts. No wire, save, checkpoint or cooked format changes, and the engine keeps no baked tile across runs.

### Changed

- **Breaking:** The engine is built on Maul Nav 1.2.0, from 0.2.0, vendored from its release archive with its SHA-256 checked. `rawframe/navigation`'s API does not change. What a game sees change ([#222](#222)):
  - A navmesh's tile bytes and fingerprint change: tiles bake under generator 6 and tile format 4. The fingerprint a server reports in its `navigation_summary` record moves.
  - A walkable strip one cell thin now gets a polygon, so a path may cross it, and heights along long edges are sampled more finely, which reaches the height of a path's points over uneven ground. Paths over the same polygons are otherwise unchanged.
  - Avoidance velocities are rounded to binary32 and never pass an agent's maximum speed, so a game's agents that avoid each other in `rawframe.navigation.avoid`, as tycoon's customers do, move by slightly different amounts each step.
  - A navigation query past 1e100 m from the navmesh's origin finds nothing.
- The engine is built on Maul UI 1.3.3 and Maul Unicode 1.1.2, each vendored from its release archive with its SHA-256 checked. Maul Window, Maul Tween and Maul RHI stay at 0.12.0, 1.0.0 and 0.15.0. Layouts, styles, draw lists, line breaks, shaped text, glyph images and the accessibility tree are unchanged; line breaking takes 1 to 2% longer, since Maul Unicode now checks every iterator it is given ([#221](#221)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant