Prototype spatial GPS edge weights

This commit is contained in:
2026-06-26 21:24:24 -05:00
parent 55f4087458
commit 56ef79d14a
6 changed files with 982 additions and 37 deletions
+42 -4
View File
@@ -6,8 +6,8 @@ Current date/time context: 2026-06-26, local timezone America/Chicago.
- Installed RED4ext state is safe: the game plugin directory contains
`EdgeWeightGPS.dll.disabled`, not an active `EdgeWeightGPS.dll`.
- Latest committed safe source before this update: `d68a874 Disable unstable
traffic scorer hook`.
- Latest committed safe source before this update: `55f4087 Document VAND GPS
search graph`.
- Current source has `kEnableGpsTrafficEdgeWeightPatch = false` and
`kEnableGpsLocalSearchHooks = false`.
- The real route search is confirmed at `0x44f054`, called from
@@ -44,6 +44,42 @@ Current date/time context: 2026-06-26, local timezone America/Chicago.
highway/arterial corridors, or patch the navigation VAND graph/classes after
extracting the relevant streamingsector resources.
## 2026-06-26 Spatial Edge-Cost Prototype
- Added `tools/generate_spatial_edge_grid.py`, which combines
`work/raw-segment-json/all.traffic_persistent.json` lane flags with
`work/traffic-companions/json/all.lane_polygons.json` polygons.
- Generated
`red4ext/EdgeWeightGPS/src/GeneratedSpatialRoadGrid.hpp`. The grid is
332 x 361 cells at 32 m/cell, origin `(-4640, -7392)`, with final cell
counts `H=7574`, `R=19617`, `G=20`, `P=996`, `unknown=91645`.
- Added disabled-by-default runtime plumbing behind
`kEnableGpsSpatialEdgeWeightPatch = false`. When enabled, it attaches only
`GPSRouteProducer 0x44cc7c` and `GPSEdgeCost 0x44f838` unless the old verbose
local-search probe is also enabled.
- The patch reads search-state vectors as `(world X, height, world Y)`, samples
the current endpoint, neighbor endpoint, and midpoint, chooses the strongest
spatial road class, then applies a multiplier after native `0x44f838`
returns. Current test multipliers are highway `0.62`, road `1.0`,
GPS-only `1.20`, pavement `1.35`, unknown `1.05`.
- The patch intentionally does not rewrite VAND point class bits. Static
materialization code branches on those bits later, so changing them in data is
riskier than adjusting cost at the edge-cost vtable slot.
- Fixed `DetourGpsEdgeCost` logging: native `0x44f838` indexes the provider
cost table by neighbor point class, not source point class.
- Build verification passed in the Fedora toolbox:
`toolbox run -c 2077 tools/build_red4ext_shim.sh`.
- The resulting DLL remains uninstalled. Game install still has only
`EdgeWeightGPS.dll.disabled`, not an active plugin DLL.
Important caveat:
- Runtime logs show long visible GPS routes are decomposed into many
`0x44cc7c` local searches. The `ret_rva=0x70aa22` caller with
`query_f78=10` and `query_ptr98_rva=0x8d76c4` mostly produces tiny connector
searches, not the whole route. Do not gate the spatial patch only on that
caller unless a later test proves it covers the desired route segment.
## Current Installed State
- Installed RED4ext plugin is disabled:
@@ -76,12 +112,14 @@ In `red4ext/EdgeWeightGPS/src/Main.cpp`:
- `kEnableGpsQueryLifecycleHooks = true`
- `kEnableGpsRouteJobLifecycleHooks = false`
- `kEnableGpsRouteJobStartTrace = false`
- `kEnableGpsSpatialEdgeWeightPatch = false`
- `kEnableGpsLocalSearchHooks = false`
- `kEnableRouteUiTraceHooks = false`
If rebuilt with the current defaults, active hooks are query/async lifecycle
only. The local search/cost hooks below require `kEnableGpsLocalSearchHooks =
true`:
only. The spatial edge-cost patch requires `kEnableGpsSpatialEdgeWeightPatch =
true`. The full local search/cost hooks below require
`kEnableGpsLocalSearchHooks = true`:
- query lifecycle:
- `GPSQuerySubmit 0x70a42c`
+21 -8
View File
@@ -66,14 +66,23 @@ Current behavior:
Active build note:
- The currently installed build is read-only. It disables provider class
multiplier overrides and enables GPS query lifecycle tracing plus local GPS
search tracing.
- The currently installed build is disabled on disk:
`EdgeWeightGPS.dll.disabled`. There is no active `EdgeWeightGPS.dll` in the
game plugin directory.
- Source defaults are still read-only for route behavior. They disable provider
class multiplier overrides and the spatial edge-cost patch, while keeping GPS
query lifecycle tracing available for probe builds.
- The async route-job hooks are split behind `kEnableGpsRouteJobLifecycleHooks`
and are disabled in the active probe; this keeps the log focused on
`0x70a42c` submissions, `0x7094b8` result records, and local
search/provider-cost stats.
- It hooks `GPSSearch` (`0x44f054`), `GPSRouteProducer` (`0x44cc7c`),
- `kEnableGpsSpatialEdgeWeightPatch` is a disabled-by-default implementation
path. When enabled, it hooks `GPSRouteProducer` (`0x44cc7c`) and
`GPSEdgeCost` (`0x44f838`), samples a generated traffic-lane spatial grid
using the search-state coordinates, and applies highway/road/pavement
multipliers to returned edge costs.
- The old verbose local-search probe is still available behind
`kEnableGpsLocalSearchHooks`. It hooks `GPSSearch` (`0x44f054`),
`GPSResolveHandle` (`0x44e1a8`), `GPSEdgeCost` (`0x44f838`), and the two
cost-provider filters `0x44ff68`/`0x450b08`.
- The filter probe records pass/fail counts by point class and by the
@@ -345,10 +354,14 @@ Current static producer lead:
`(*(routePoint + 0x13) & 0x3f)`. The GPS planner is therefore weighted, but
by a compact baked road-class table, not by traffic `maxSpeed` or lane
connection probabilities.
- The active probe now hooks `0x44f054` and `0x44f838` read-only. It logs the
live provider vtable, the 64 class multipliers, start/target handles, and a
capped sample of edge-cost calls with point class IDs, masks, handles, and
returned costs.
- The read-only local-search probe can still hook `0x44f054` and `0x44f838` to
log the live provider vtable, the 64 class multipliers, start/target handles,
and a capped sample of edge-cost calls with point class IDs, masks, handles,
and returned costs.
- The new spatial edge-cost path is implemented but disabled by default. Its
generated grid is built from traffic lane polygons/flags, not from VAND point
classes, so it can bias the search toward highway cells without rewriting
VAND metadata that later materialization code branches on.
Autodrive finding:
+27 -13
View File
@@ -836,18 +836,30 @@ renamed to `EdgeWeightGPS.dll.disabled`.
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:
function.
- 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
without desynchronizing the planner and renderer
The current prototype therefore targets `0x44f838` instead. It generates a
compact spatial grid from traffic lane polygons/flags and samples that grid
from the search-state coordinates passed to the edge-cost callback. The first
test multipliers are:
```text
highway 0.62
road 1.00
GPSOnly 1.20
pavement 1.35
unknown 1.05
```
This deliberately leaves VAND point class bits untouched. The materialization
path branches on those bits after search, so rewriting them in data could alter
route construction or instructions as well as cost.
Runtime logs also show that long visible routes are decomposed into many local
`0x44cc7c` route-producer searches. The tempting world-map caller
`ret_rva=0x70aa22` mostly produced tiny connector searches in the resolver
logs, so the first spatial test should patch edge cost across route-producer
calls instead of gating only on that caller.
## Future Improvements
@@ -861,8 +873,10 @@ Better versions of this mod could:
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`
- tune the generated spatial grid resolution and multipliers after in-game A/B
tests
- add route-mode gating if the spatial edge-cost hook affects pedestrians,
autodrive, or other local navigation too broadly
- 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