Files
2077-gps-mod/docs/compaction-handoff.md
T
2026-06-20 21:21:32 -05:00

5.7 KiB

Compaction Handoff

Current date/time context: 2026-06-20/21, local timezone America/Chicago.

Current Installed State

  • Latest commit: 64ddf7a Trace GPS provider filters.
  • Installed RED4ext plugin: Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.dll.
  • Installed build is read-only. It should not alter route behavior.
  • Plugin log is cleared and empty before the next test: Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.log.
  • Game is cold as of the latest user message.

New Toolbox Tools

The Fedora toolbox 2077 now has:

  • rizin 0.7.4
  • python3-capstone 5.0.5
  • python3-pefile 2024.8.26

If adding Python-only tooling, create/use a venv and maintain requirements.txt instead of one-off pip installs.

Active Probe Configuration

In red4ext/EdgeWeightGPS/src/Main.cpp:

  • kEnableGpsProviderClassPatch = false
  • kEnableGpsProviderClassPatchTrace = false
  • kEnableGpsTraceHooks = true
  • kEnableGpsQueryLifecycleHooks = true
  • kEnableGpsRouteJobLifecycleHooks = false
  • kEnableGpsLocalSearchHooks = true
  • kEnableRouteUiTraceHooks = false

Active hooks:

  • query lifecycle:
    • GPSQuerySubmit 0x70a42c
    • GPSQueryDispatch 0x70a570
    • GPSQueryResultFetch 0x7094b8
    • GPSQueryStatus 0xaa5704
  • GPSSearch 0x44f054
  • GPSRouteProducer 0x44cc7c
  • GPSEdgeCost 0x44f838
  • filtered provider filter 0x44ff68
  • base provider filter 0x450b08

Async route-job hooks (0x8d17d8, 0x126b28, 0x883cd8, 0x11f5f60, 0x818ba8) are split behind kEnableGpsRouteJobLifecycleHooks and currently disabled to keep the next run focused on solved route result records plus local search/cost stats.

The filter probe records pass/fail totals by GPS point class and by bits in point + 0x10, tested against provider masks at +0x108 and +0x10a.

Why This Probe Exists

Data and behavior patches tested so far:

  • Traffic maxSpeed patches affected NPC freeway speed but not GPS route choice.
  • Lane connection probability patches did not change GPS route choice.
  • Global cost-table patch produced weird routing but mostly confirmed we were near the solver, not a clean highway preference knob.
  • Provider class override patches fired correctly but produced no perceptible route change after preserving vanilla class 14.

Static disassembly clarified the provider split:

  • Provider vtable slot +0x08 is an admissibility filter, not cost.
  • Provider vtable slot +0x10 is geometric edge cost, multiplying distance by provider[0x08 + class * 4].
  • We patched only slot +0x10 inputs. If slot +0x08 filtering dominates the candidate graph, those cost changes can fire and still not visibly steer GPS.

Important Native Findings

GPS pipeline landmarks:

  • shared query submit: 0x70a42c
  • query dispatch: 0x70a570
  • route producer: 0x44cc7c
  • local search loop: 0x44f054
  • edge cost: 0x44f838
  • query result fetch: 0x7094b8

The search loop is A*/Dijkstra-like:

  • initialize/open list with start node
  • pop best node
  • check destination handle
  • traverse adjacent lane handles
  • call provider filter
  • call provider cost
  • update candidate cost/best predecessor

Provider details:

  • Base provider vtable RVA: 0x2ae6120
  • Filtered provider vtable RVA: 0x2ae60f8
  • Base provider constructor 0x451158 fills 64 class multipliers with 1.0 and sets mask +0x108 to 0xffff.
  • Filtered provider constructor 0x44b9b4 changes vtable to 0x2ae60f8.
  • Filtered provider commonly has class 14 = 0.05; preserve this unless deliberately testing it.

Route result records:

  • 0x7094b8 output records are 0x28-byte route segment records.
  • h00 in those records matches worldTrafficLanePersistent.nodeRefHash.
  • Route records can be joined to work/raw-segment-json/all.traffic_persistent.json.
  • The low byte of route-record u08 is not the same as the search point class.

Next User Test

Ask the user to run the usual routine:

  1. Launch game.
  2. Hit Continue on main menu.
  3. Hit Space to Continue.
  4. Wait for minimap/GPS to settle.
  5. Open map.
  6. Plot the usual three routes plus one custom highway-ish target.
  7. Close game.

Ask for timestamps for:

  • Space-to-Continue
  • map open
  • each route click
  • game cold

After the run:

  1. Archive the log to logs/EdgeWeightGPS_provider_filter_probe_<time>.log.
  2. Inspect GPSQuerySubmit/GPSQueryResultFetch lines for tracked route IDs from submit return RVA 0x8d20d4, especially successful 0x7094b8 records with gpsResult_ptr28_rec40.
  3. Inspect GPSSearch result lines for filterPassClasses, filterFailClasses, filterPassMaskBits, and filterFailMaskBits.
  4. Compare filter pass/fail classes to edgeSrcClasses and route output records. The key question is whether the class/mask filter is selecting the graph subset before cost weighting can matter.

Useful Commands

Build/install:

toolbox run -c 2077 ./tools/build_red4ext_shim.sh
toolbox run -c 2077 ./tools/install_red4ext_shim.sh

Clear plugin log:

truncate -s 0 '/var/home/salt/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.log'

Inspect current plugin log:

tail -n 240 '/var/home/salt/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Cyberpunk 2077/red4ext/plugins/EdgeWeightGPS/EdgeWeightGPS.log'

Disassemble with rizin in toolbox:

toolbox run -c 2077 rizin -q -A '/var/home/salt/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common/Cyberpunk 2077/bin/x64/Cyberpunk2077.exe'

Caution

  • Do not revert user changes or old untracked logs unless explicitly asked.
  • Commit early/often.
  • Do not install Python packages one by one with pip; use requirements.txt.