Generated nav link configs (NavLinkJumpConfigs) on placed RecastNavMesh always differ from CDO, so Project Settings changes never reach existing levels

Summary

When automatic nav link generation is enabled and a LinkProxyClass is set, every RecastNavMesh placed in a level ends up with a NavLinkJumpConfigs array that differs from the class default object, even if the user never edits it. The whole array is therefore saved in the level. After that, changes made to NavLinkJumpConfigs in Project Settings (for example, adding a new jump config) are never applied to that level’s nav mesh, and building paths does not pick them up either.

Root cause

FNavLinkGenerationJumpConfig stores runtime state in the same UPROPERTY struct as the user settings:

- LinkProxyId (UPROPERTY, serialized)

- LinkProxy (UPROPERTY(SkipSerialization))

- bLinkProxyRegistered (UPROPERTY(SkipSerialization))

In the editor, ARecastNavMesh::PostRegisterAllComponents (and PostLoadPreRebuild on rebuild) calls CreateAndRegisterJumpLinksProxy, which sets LinkProxyId to a generated unique id, sets LinkProxy to a new proxy object, and sets bLinkProxyRegistered to true. On the CDO these stay at Id=0 / nullptr / false.

When the level is saved, UStruct::SerializeVersionedTaggedProperties writes NavLinkJumpConfigs only if FArrayProperty::Identical against the CDO fails. The comparison goes through UScriptStruct::CompareScriptStruct, which compares every property of the struct, including SkipSerialization ones. Because the three fields above always differ, the array is never identical to the CDO and is always saved in full. On load, the serialized array overrides the CDO value, so later Project Settings changes are ignored.

Steps to Reproduce

1. In Project Settings > Navigation Mesh, enable Generate Nav Links and set a Link Proxy Class on the single default entry of Nav Link Jump Configs.

2. Open a level, add a NavMeshBoundsVolume, build paths, and save the level.

3. In Project Settings > Navigation Mesh, add a second entry to Nav Link Jump Configs (different jump parameters, also with a Link Proxy Class).

4. Reopen the level and build paths.

Expected

The level’s RecastNavMesh uses both jump configs from Project Settings, and links are generated for the new config.

Actual

The level’s RecastNavMesh still has only the original entry. No links are generated for the new config.

Thank you for the detailed write-up. I have confirmed this against source, and it is a bug. We have even encountered this internally on FN where we added a CallInEditor button to reset the link config settings.

One correction: the SkipSerialization fields are not what triggers it. LinkProxyId alone is enough, and 5.7, where the other two fields are Transient, behaves the same way. LinkProxyId also has to stay serialized. It is baked into the tile data as the off-mesh connection userId, so a WithIdentical override that ignores it would break the proxy lookup on load until the next rebuild.

As a workaround, per level:

  1. Reset to Default on Nav Link Jump Configs on the RecastNavMesh actor, then save.
  2. Reload the level, so the proxy is recreated cleanly.
  3. Build Paths and save.

This needs repeating after each Project Settings change.

I will be passing this on to the team. Let me know if that approach works out for you.

-James

I’m glad to hear that! If you have any other questions or run into other issues, please let us know.

-James

Thanks. That approach works out for me.