285 lines
8.1 KiB
Markdown
285 lines
8.1 KiB
Markdown
# Cyberpunk 2077 Traffic/GPS System Debrief
|
|
|
|
This is based on the public NativeDB RTTI dump, WolvenKit exports from the local
|
|
Steam install, and the generated `all.traffic_persistent` resource from the
|
|
Phantom Liberty archive.
|
|
|
|
## The High-Level Shape
|
|
|
|
Cyberpunk appears to split "navigation" into at least two major domains:
|
|
|
|
- pedestrian/NPC navigation over navmesh resources
|
|
- vehicle/GPS routing over generated traffic lane resources
|
|
|
|
The public script/native type dump exposes `worldNavigationScriptInterface` and
|
|
`AINavigationSystem` methods for navmesh path queries, but those are character
|
|
navigation tools. They are not the car GPS. The car GPS system type exists as
|
|
`gamegpsGPSSystem`, but its public RTTI surface does not expose planner methods
|
|
that a REDscript mod can override.
|
|
|
|
That means the practical mod surface is data, not algorithm replacement. The
|
|
native `gamegpsGPSSystem` likely builds or queries a graph from traffic lane
|
|
resources, then renders a GPS line from the selected lane path.
|
|
|
|
## Relevant Game Types
|
|
|
|
The useful RTTI types are:
|
|
|
|
- `gamegpsGPSSystem`
|
|
- `gamegpsSettings`
|
|
- `worldTrafficPersistentResource`
|
|
- `worldTrafficPersistentData`
|
|
- `worldTrafficLanePersistent`
|
|
- `worldTrafficLanePlayerGPSInfo`
|
|
- `worldTrafficLanePersistentFlags`
|
|
|
|
`gamegpsSettings` contains display/refresh settings:
|
|
|
|
- `lineEffectOnFoot`
|
|
- `lineEffectVehicle`
|
|
- `fixedPathOffset`
|
|
- `fixedPortalMappinOffset`
|
|
- `pathRefreshTimeInterval`
|
|
- `lastPlayerNavmeshPositionRefreshTimeIntervalSecs`
|
|
- `maxPathDisplayLength`
|
|
|
|
Those affect GPS presentation and refresh behavior, not edge selection.
|
|
|
|
`worldTrafficPersistentResource` is the key resource type. Its root chunk has
|
|
`data: worldTrafficPersistentData`, which has:
|
|
|
|
- `lanes: array<worldTrafficLanePersistent>`
|
|
- `neighborGroups: array<array<Uint16>>`
|
|
|
|
Each lane has the actual graph metadata:
|
|
|
|
- `outLanes`
|
|
- `inLanes`
|
|
- `outline`
|
|
- `accumulatedLengths`
|
|
- `crowdCreationInfo`
|
|
- `maxSpeed`
|
|
- `deadEndStart`
|
|
- `length`
|
|
- `width`
|
|
- `area`
|
|
- `flags`
|
|
- `subGraphId`
|
|
- `playerGPSInfo`
|
|
- `neighborGroupIndex`
|
|
- `nodeRefHash`
|
|
- `laneNumber`
|
|
- `seqNumber`
|
|
- `isReversed`
|
|
- `roadMaterials`
|
|
- `polygon`
|
|
|
|
`playerGPSInfo` contains:
|
|
|
|
- `subGraphId`
|
|
- `stronglyConnectedComponentId`
|
|
|
|
That strongly suggests the GPS planner has a precomputed connectivity graph and
|
|
uses connected-component/subgraph metadata to reject impossible paths quickly.
|
|
|
|
## Resource Location
|
|
|
|
In this install, the important resource is:
|
|
|
|
```text
|
|
base\worlds\03_night_city\sectors\_generated\traffic\all.traffic_persistent
|
|
```
|
|
|
|
It exists in both the basegame Night City archive and the Phantom Liberty Night
|
|
City archive. For a Phantom Liberty install, the EP1 archive version should win:
|
|
|
|
```text
|
|
archive/pc/ep1/ep1_1_nightcity.archive
|
|
```
|
|
|
|
WolvenKit can extract, serialize, deserialize, and pack this resource on Linux
|
|
through `wolvenkit.cli` 8.18.1.
|
|
|
|
## Lane Flags
|
|
|
|
`worldTrafficLanePersistentFlags`:
|
|
|
|
```text
|
|
FromRoadSpline = 1
|
|
Bidirectional = 2
|
|
PatrolRoute = 4
|
|
Pavement = 8
|
|
Road = 16
|
|
Intersection = 32
|
|
NeverDeadEnd = 64
|
|
TrafficDisabled = 128
|
|
CrossWalk = 256
|
|
GPSOnly = 512
|
|
ShowDebug = 1024
|
|
Blockade = 2048
|
|
Yield = 4096
|
|
NoAIDriving = 8192
|
|
Highway = 16384
|
|
NoAutodrive = 32768
|
|
```
|
|
|
|
The important flags for GPS weighting are `Road`, `Intersection`, `GPSOnly`, and
|
|
`Highway`. The important flags to avoid touching are `Pavement`, `CrossWalk`,
|
|
`TrafficDisabled`, and `Blockade`.
|
|
|
|
## Observed Lane Distribution
|
|
|
|
The local EP1 `all.traffic_persistent` resource contains `33,952` lanes when
|
|
extracted from the archive's raw compressed segment.
|
|
|
|
Observed `maxSpeed` distribution:
|
|
|
|
```text
|
|
all lanes:
|
|
1: 12852
|
|
15: 12107
|
|
10: 5859
|
|
17: 3027
|
|
14: 96
|
|
8: 8
|
|
9: 2
|
|
4: 1
|
|
|
|
highway lanes:
|
|
17: 2700
|
|
15: 164
|
|
1: 2
|
|
|
|
road lanes:
|
|
15: 11943
|
|
10: 671
|
|
17: 327
|
|
14: 96
|
|
8: 4
|
|
|
|
pavement lanes:
|
|
1: 12848
|
|
10: 5188
|
|
8: 4
|
|
9: 2
|
|
4: 1
|
|
```
|
|
|
|
The values are not real-world mph/kph. They are compact game-scale lane speeds.
|
|
Because highways are already mostly `17` and normal roads are mostly `15`, a
|
|
large value like `90` would be reckless. The mod currently uses conservative
|
|
defaults:
|
|
|
|
- highway speed target: `25`
|
|
- road speed floor: `15`
|
|
|
|
That changes `2,519` lanes:
|
|
|
|
- `2,429` highway boosts
|
|
- `90` road floor boosts
|
|
|
|
It leaves pavement/crosswalk/blocked/disabled lanes alone.
|
|
|
|
## What The Mod Actually Does
|
|
|
|
The mod does not replace the pathfinding algorithm.
|
|
|
|
It edits the traffic graph's lane metadata so the native GPS planner has better
|
|
cost inputs. If the planner factors `maxSpeed` into traversal cost in the usual
|
|
way, then a segment with the same physical length but higher `maxSpeed` becomes
|
|
cheaper. That nudges the native planner toward highways and high-capacity roads
|
|
without having to hook the planner itself.
|
|
|
|
Conceptually:
|
|
|
|
```text
|
|
old cost ~= lane.length / lane.maxSpeed
|
|
new cost ~= lane.length / patched_lane.maxSpeed
|
|
```
|
|
|
|
The actual game formula is native and not exposed in RTTI, so this is an
|
|
inference. The inference is supported by the presence of `maxSpeed`, `length`,
|
|
connectivity data, and GPS-specific lane graph metadata in the same resource.
|
|
|
|
## Why Not Just Write A New A*?
|
|
|
|
A standalone A* is easy. Integrating it into Cyberpunk's GPS is the hard part.
|
|
|
|
The game already has:
|
|
|
|
- traffic lane connectivity
|
|
- lane polygons/outlines
|
|
- player lane matching
|
|
- destination lane matching
|
|
- dynamic refresh
|
|
- minimap/world map rendering
|
|
- quest mappin target integration
|
|
- portal/entrance handling
|
|
- route line effects
|
|
|
|
The script-facing `gamegpsGPSSystem` type does not expose hooks for replacing
|
|
that pipeline. A custom planner would still need a way to feed its result back
|
|
into the GPS renderer and route-following systems. Without native RED4ext hooks,
|
|
that is much riskier than patching the resource data the existing pipeline
|
|
already consumes.
|
|
|
|
## Packaging Flow
|
|
|
|
The working flow is:
|
|
|
|
1. Read the archive index and extract the exact compressed segment for resource
|
|
hash `3419764573789342681`.
|
|
2. Decompress that KARK segment with WolvenKit CLI's Oodle command.
|
|
3. Serialize the CR2W resource to JSON.
|
|
4. Patch lane `maxSpeed` values.
|
|
5. Deserialize the patched JSON back to CR2W.
|
|
6. Put the CR2W back under the original resource path.
|
|
7. Pack an archive.
|
|
8. Place the archive in `archive/pc/mod`.
|
|
|
|
Do not use plain `cp77tools extract` for this resource. In this install,
|
|
WolvenKit CLI extracted a `9,984,106` byte CR2W, while the stock archive segment
|
|
declares and decompresses to `10,673,648` bytes. A vanilla control archive built
|
|
from the smaller extraction crashed during load. A vanilla control archive built
|
|
from the raw segment reached the game and the GPS worked normally.
|
|
|
|
The generated archive currently contains exactly:
|
|
|
|
```text
|
|
base\worlds\03_night_city\sectors\_generated\traffic\all.traffic_persistent
|
|
```
|
|
|
|
## Risks And Unknowns
|
|
|
|
The main unknown is the native GPS cost formula. If it ignores `maxSpeed`, this
|
|
patch will not meaningfully change routes. If it uses speed but with caps or
|
|
category penalties, the effect may be subtle. If it uses `maxSpeed` for both GPS
|
|
and traffic simulation, the patch may also affect some traffic behavior on
|
|
highways.
|
|
|
|
The Oodle library was not available inside the toolbox, so WolvenKit packed with
|
|
its Kraken fallback. A vanilla full-resource control archive loaded in game and
|
|
GPS worked normally, so the earlier crash was caused by bad extracted data rather
|
|
than the Kraken-packed archive container.
|
|
|
|
The patch targets the EP1 traffic resource because Phantom Liberty is installed.
|
|
On a non-PL install, the basegame archive resource would need to be patched
|
|
instead.
|
|
|
|
## Future Improvements
|
|
|
|
Better versions of this mod could:
|
|
|
|
- build district-specific presets
|
|
- increase highway speed to `22`, `25`, or `30` based on playtesting
|
|
- lower intersection penalties only where highway ramps are misclassified
|
|
- inspect `lane_connections` to identify ramp/arterial topology
|
|
- patch `GPSOnly` connector lanes separately
|
|
- generate a diff report of every changed lane with flags, length, speed, and
|
|
graph component
|
|
- ship multiple archives: conservative, strong, and experimental
|
|
|
|
The next practical step is in-game route testing between known bad waypoint
|
|
pairs, especially cases where vanilla GPS exits highways too early or cuts
|
|
through surface streets.
|