Document full GPS solver lead
This commit is contained in:
@@ -873,15 +873,139 @@ identical record counts. Therefore `0x44f838` remains a real local graph edge
|
||||
cost callback, but changing it does not change the final selected route records
|
||||
for the tested full player GPS routes.
|
||||
|
||||
Static disassembly after that failure found a separate, broader async traffic
|
||||
solver upstream of final route-record materialization. The useful path is:
|
||||
|
||||
```text
|
||||
map route submit 0x8d20d4
|
||||
shared query submit 0x70a42c
|
||||
query dispatch 0x70a570
|
||||
async request enqueue 0x8d17d8 / 0x8d1880
|
||||
worker queue loop 0x126c06
|
||||
candidate/projection setup 0x883cd8
|
||||
solver start wrapper 0x1caa7a0
|
||||
solver candidate initialization 0x88367c
|
||||
route job start/advance 0x818928
|
||||
solver advance loop 0x40ade4
|
||||
neighbor expansion 0x40b304 / 0x40b540
|
||||
relax/update helper 0x40ba58
|
||||
output route build 0x818ba8
|
||||
```
|
||||
|
||||
This path is distinct from the local `0x44f054` VAND search. The worker around
|
||||
`0x126c06` pulls queued route requests from `routeSystem+0x47c0`. Depending on
|
||||
state it either calls `0x818928` directly or first calls `0x883cd8`, which
|
||||
resolves/projects the start and end positions into candidate arrays. The
|
||||
wrapper at `0x1caa7a0` starts the solver by storing the route mode at
|
||||
`solver+0xc4`, writing route masks at `solver+0xc0/+0xc2`, copying candidate
|
||||
arrays into `solver+0x70/+0x80`, and calling `0x88367c`.
|
||||
|
||||
`0x88367c` marks start candidate lane/node records with `node+0x9a |= 1`, marks
|
||||
end candidates with `node+0x9a |= 2`, computes initial node-state costs at
|
||||
offsets `+0x10/+0x14`, and queues initial states into the open list at
|
||||
`solver+0xd0`.
|
||||
|
||||
`0x40ade4` is the full-route solver advance loop. It processes the open list
|
||||
while `solver+0xdc != 0`, calls helpers including `0x40b304` and `0x40b540` to
|
||||
expand neighbors, and marks completed node states with byte `+0x22 = 2`.
|
||||
Current lane/node records are addressed as:
|
||||
|
||||
```text
|
||||
laneNode = solver + 0xf8 + currentNodeIndex * 0xa0
|
||||
```
|
||||
|
||||
`0x40b304` walks one neighbor list from the current record; `0x40b540` walks a
|
||||
second list with a slightly different edge-record shape. Both filter neighbors
|
||||
through `0x40b774`, which reads the 16-bit lane/node flags at `node+0x88`:
|
||||
|
||||
```text
|
||||
pass iff (solver+0xc0 & flags88) != 0
|
||||
and (solver+0xc2 & flags88) == 0
|
||||
```
|
||||
|
||||
The main cost equation visible in `0x40b304` is:
|
||||
|
||||
```text
|
||||
g = current.g + progress_delta
|
||||
* 0x40bb40(solver, currentLaneNode)
|
||||
* directionMultiplier
|
||||
h = distance(neighborPosition, solverTargetPosition)
|
||||
f = g + h
|
||||
```
|
||||
|
||||
`directionMultiplier` is normally `1.0`; for reverse/special progress branches
|
||||
it can come from `0x40bb00(solver, currentLaneNode)` or become `FLT_MAX`.
|
||||
`0x40ba58` is the relax/update function. When a candidate improves, it writes
|
||||
`f` to node-state `+0x14`, `g` to `+0x10`, progress to `+0x18`, predecessor to
|
||||
`+0x1e`, marks state byte `+0x22 = 1`, and pushes the state back into
|
||||
`solver+0xd0`.
|
||||
|
||||
The decisive vanilla multiplier is `0x40bb40`. For driving-style modes it reads
|
||||
the same `node+0x88` flags and handles them roughly as:
|
||||
|
||||
```text
|
||||
if mode == 1:
|
||||
pavement flag 0x0008 -> 1.0
|
||||
otherwise -> 1.5
|
||||
|
||||
if mode in {0, 2, 4}:
|
||||
if Road flag 0x0010:
|
||||
base = 1.0 or 1.2 depending on a low node flag
|
||||
if node+0x93 == 1: base *= 1.1
|
||||
return base
|
||||
else:
|
||||
return table[solver+0x54]
|
||||
|
||||
fallback:
|
||||
return 1.0
|
||||
```
|
||||
|
||||
The non-road table at `0x3154d28` starts:
|
||||
|
||||
```text
|
||||
10.0, 6.0, 2.0, 0.0, 4.0, 2.5, 1.5, 0.0
|
||||
```
|
||||
|
||||
The auxiliary multiplier at `0x40bb00` usually returns `1.1` for road cases,
|
||||
or, for one mode-2 branch, reads a smaller table at `0x3154d38`:
|
||||
|
||||
```text
|
||||
4.0, 2.5, 1.5, 0.0
|
||||
```
|
||||
|
||||
The important design finding is that vanilla has a real edge-cost surface in
|
||||
the final async solver, but it does not special-case the `Highway` flag
|
||||
(`0x4000`). A highway lane that is also a road is simply a road. That matches
|
||||
the original symptom: the solver is weighted enough to avoid obviously invalid
|
||||
surfaces, but it has no strong reason to stay on highway corridors for long
|
||||
vehicle trips.
|
||||
|
||||
The current RED4ext prototype therefore hooks `0x40bb40` and only changes
|
||||
driving-mode calls (`solver/job + 0xc4 == 2`). It reads flags from
|
||||
`node+0x88`, leaves vanilla results intact for ordinary roads and unknown
|
||||
surfaces, and applies this diagnostic multiplier set:
|
||||
|
||||
```text
|
||||
Highway 0.35
|
||||
GPSOnly 1.10
|
||||
Pavement 1.25
|
||||
Other 1.00
|
||||
```
|
||||
|
||||
`0x40bb00` is currently hooked only for signature logging. If the node
|
||||
multiplier test moves routes but produces weird reverse/turnaround behavior,
|
||||
the next step is to tune or gate the auxiliary multiplier as well.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Better versions of this mod could:
|
||||
|
||||
- identify the broader async traffic route search/expansion loop around the
|
||||
`0x8d25xx` to `0x8d49xx` cluster
|
||||
- disassemble the full map-route submit/result chain around `0x8d20d4`,
|
||||
`0x70a42c`, `0x70a570`, `0x7094b8`, and the `0x520783` result drain to find
|
||||
the route selection or copy step before final records are emitted
|
||||
- tune the `0x40bb40` solver multiplier after live A/B testing confirms visible
|
||||
route changes
|
||||
- inspect and, if needed, patch `0x40bb00` so reverse/special progress branches
|
||||
receive the same road-class policy as forward expansion
|
||||
- add a tiny live-tuning file for the solver multipliers, similar to the earlier
|
||||
`spatial_weights.bin`, once the hook is confirmed stable
|
||||
- replace the crashing `0x8d46cc` detour with a narrower inline patch or a
|
||||
safer caller-side hook
|
||||
- decode enough of the `VAND` graph to join navigation points to traffic lane
|
||||
|
||||
Reference in New Issue
Block a user