Document VAND GPS search graph
This commit is contained in:
@@ -731,6 +731,60 @@ from `all.traffic_persistent`, which confirms that the displayed/player GPS path
|
||||
is backed by the traffic lane graph even though `maxSpeed` and connection
|
||||
probability patches did not affect route choice.
|
||||
|
||||
Later static work found the actual graph-search loop upstream of those result
|
||||
records:
|
||||
|
||||
```text
|
||||
route producer 0x44cc7c
|
||||
packed-handle resolver 0x44e1a8
|
||||
path search loop 0x44f054
|
||||
path materializer 0x44eb68
|
||||
heuristic distance helper 0x44f7bc
|
||||
edge-cost callback 0x44f838
|
||||
base provider filter 0x450b08
|
||||
filtered provider filter 0x44ff68
|
||||
```
|
||||
|
||||
`0x44f054` is an A*/Dijkstra-style open-list search. It validates packed start
|
||||
and target handles, pushes the start node, repeatedly pops the lowest-priority
|
||||
state, checks the target handle, traverses adjacent handles from the current
|
||||
navigation segment, calls a provider filter through vtable slot `+0x08`, calls
|
||||
edge cost through vtable slot `+0x10`, and records the predecessor/cost when a
|
||||
neighbor improves.
|
||||
|
||||
Both the base and filtered cost providers use `0x44f838` for slot `+0x10`.
|
||||
That function computes geometric distance between the current and neighbor
|
||||
points and multiplies it by:
|
||||
|
||||
```text
|
||||
provider[0x08 + ((neighborPoint[0x13] & 0x3f) * 4)]
|
||||
```
|
||||
|
||||
The base provider constructor at `0x451158` initializes all 64 class
|
||||
multipliers to `1.0`. The filtered provider constructor at `0x44b9b4` reuses the
|
||||
same edge-cost function and mainly adds route masks/special zones. For normal
|
||||
map routes, this means the live GPS edge cost is effectively geometric distance
|
||||
over a compact navigation graph, not `lane.length / lane.maxSpeed`.
|
||||
|
||||
The packed handles resolved by `0x44e1a8` point into `VAND` navigation blobs
|
||||
loaded from `base\worlds\03_night_city\_compiled\default\navigation_*.streamingsector`.
|
||||
The 0x14-byte point record has:
|
||||
|
||||
- `+0x00`: adjacency/index field used by the search loop
|
||||
- `+0x10`: 16-bit provider mask
|
||||
- `+0x12`: small point metadata byte
|
||||
- `+0x13`: packed route point class/flags; low six bits are the class used by
|
||||
`0x44f838`
|
||||
|
||||
The helper `tools/analyze_vand_navigation.py` decodes these blobs from
|
||||
streamingsector JSON. In the three extracted navigation samples, all blobs were
|
||||
version 8, all point masks were `0x0003`, and the dominant classes were
|
||||
`1`, `4`, `5`, and `3`. That matches the runtime edge-cost logs. This explains
|
||||
why the earlier `all.traffic_persistent` patches changed vehicle simulation but
|
||||
did not steer player GPS: the route chooser is walking the baked navigation
|
||||
`VAND` graph, then later materializing/copying results that can be joined to
|
||||
traffic lane hashes.
|
||||
|
||||
Static disassembly found a small traffic candidate scorer at `0x8d46cc`:
|
||||
|
||||
```text
|
||||
@@ -779,12 +833,17 @@ RED4ext plugin was disabled. The hook is therefore disabled by default in source
|
||||
(`kEnableGpsTrafficEdgeWeightPatch = false`) and the local installed DLL is
|
||||
renamed to `EdgeWeightGPS.dll.disabled`.
|
||||
|
||||
The main takeaway is still valuable: traffic lane flags are available in native
|
||||
route-side code, and `0x8d46cc` is an excellent landmark. It is not yet a safe
|
||||
playable patch point. The next native work should avoid a full detour of this
|
||||
hot scorer and instead look for either:
|
||||
The main takeaway is still valuable, but narrower than first suspected:
|
||||
traffic lane flags are available in native route-side endpoint/candidate code,
|
||||
and `0x8d46cc` is an excellent landmark. It is not the city-wide graph-cost
|
||||
function. The safer cost target is `0x44f838`, or a data patch against the
|
||||
navigation `VAND` blobs it consumes. The next native work should avoid a full
|
||||
detour of `0x8d46cc` and instead look for either:
|
||||
|
||||
- the broader search/expansion loop that feeds candidates into this selector
|
||||
- a `0x44f838` edge-cost hook that classifies graph edges by spatial proximity
|
||||
to highway/arterial corridors
|
||||
- a navigation streamingsector patch that marks/weights selected `VAND` graph
|
||||
points or edges
|
||||
- an inline patch around the vanilla constants/branches if the selector proves
|
||||
sufficient
|
||||
- a safer result-stage rewrite only if upstream weighting cannot be patched
|
||||
@@ -798,7 +857,12 @@ Better versions of this mod could:
|
||||
`0x8d25xx` to `0x8d49xx` cluster
|
||||
- replace the crashing `0x8d46cc` detour with a narrower inline patch or a
|
||||
safer caller-side hook
|
||||
- locate any native or baked edge-cost data not exposed in RTTI
|
||||
- decode enough of the `VAND` graph to join navigation points to traffic lane
|
||||
polygons and highway flags
|
||||
- extract the full Night City navigation streamingsector set into workspace
|
||||
storage and summarize it with `tools/analyze_vand_navigation.py`
|
||||
- implement a bounded `0x44f838` edge-cost hook or `VAND` data patch using that
|
||||
join, rather than traffic `maxSpeed`
|
||||
- inspect `GPSOnly` connector lanes if a later native trace proves they are
|
||||
consumed by the player GPS
|
||||
- build district-specific presets if a real data-cost surface is found
|
||||
|
||||
Reference in New Issue
Block a user