# Modes, World Streaming & Procedural Generation

V5 ships seven games in one trench coat — a 1947 noir, a Hong Kong triad
open-world, an 1899 frontier, a Witcher-cell monster hunt, a sci-fi fleet PvP, a
steampunk whodunit, and the Mind-Palace that links them — and three jobs have to
hold that catalogue together without forking the engine per genre. **A player
has to get from "pick a thing to play" to "a session is running" through one
registry; the open world that session opens into has to stream a 256-metre
partition lattice on a console frame budget while a quarter-thousand civilians
mill around; and the side-quests, encounters, contracts, loot, decoration, and
galaxy detail that fill the dead space between authored beats have to be born
deterministically from a seed.** Those are the three subjects of this page —
mode orchestration, world streaming, and procedural generation — and they live
in four small, separately-compiled C++ modules under `V5/ue/Source/`: `V5Modes`
(the registry, build-target gate, HUD router, and esports/cabinet catalogs),
`V5Open` (the engine-integrated World-Partition streaming source, HLOD ladder,
weather/time/era data layers, and population spawners), `V5Crowd` (the
density-profile catalog and the Mass-Entity batch descriptor), and `V5Procgen`
(six seeded content generators). This page is the world-and-modes companion in
the **Multiplayer, Modes & World Simulation** group; the orientation hub is
[../V5_ARCHITECTURE.md](../V5_ARCHITECTURE.md).

## What ships, honestly

Six claims up front, so the rest reads at face value.

- **Real, compiled, value-tested.** All four modules link on this box — the
  linker has produced `Binaries/Linux/libUnrealEditor-V5Modes.so`,
  `…-V5Open.so`, `…-V5Crowd.so`, and `…-V5Procgen.so` — and the automation specs
  assert _computed_ values, not shapes: that a player at the origin pre-warms
  exactly a 3×3 = 9-cell block, that a `76800` cm streaming radius comes back
  from a 768 m request, that a heavy-rain transition at its midpoint reports
  alpha `0.5` and thins the crowd-density scalar below `0.5`, that a 48-minute
  day advanced by half lands at `18:00`, and that the same procgen seed
  reproduces a byte-identical fingerprint while a different one does not. Those
  fail against placeholders.
- **`V5Open` is the most engine-integrated of the cluster.** It is not a
  standalone planner that hands back arrays for someone else to apply — its
  streaming subsystem is a genuine `IWorldPartitionStreamingSourceProvider`
  registered with the live `UWorldPartitionSubsystem`, its population spawners
  call `UWorld::SpawnActor` and `AActor::Destroy` for real, and its weather/era
  subsystems drive the engine's `UDataLayerManager::SetDataLayerRuntimeState`.
  The tests run against a real `UWorld::CreateWorld(EWorldType::Game)`.
- **`V5Modes` is a real catalog and a real gate — but it is not a
  `UGameFeatureAction`.** The 29-mode catalog, the build-target gating, the
  per-cell HUD routing, and the plugin discovery (a real `IPluginManager` walk)
  are all genuine C++. What is _not_ here, and what V4's mode module did reach,
  is an async-asset-streaming `UGameFeatureAction` subclass: a `grep` for
  `GameFeatureAction` / `RequestAsyncLoad` / `UGameFeaturesSubsystem` across
  `V5Modes` returns nothing. V5's modes are described, discovered, and gated;
  they are not activated through the GameFeatures streaming callback in-tree.
- **`V5Procgen` is real seeded generation — but it is not PCG.** The
  architecture prose calls this "the PCG (Procedural Content Generation)
  pipeline"; the shipped code is six hand-written `FRandomStream` generators
  with no PCG dependency at all. `V5Procgen.Build.cs` links only
  `Core/CoreUObject/Engine/V5Core`, and the only `pcg` token in the module is an
  id-prefix string (`pcg.<cell>.<x>.<y>`). The determinism and the content are
  real; the "PCG" naming is aspirational.
- **The crowd density side is Mass-_linked_, not Mass-_driven_.** `V5Crowd`'s
  `Build.cs` lists `MassEntity`, but a `grep` for `FMassEntityManager`,
  `FMassEntityHandle`, or any `UMassProcessor` returns nothing.
  `BuildPopulationBatch` produces a _descriptor_ — a lightweight entity count, a
  spacing, and a named `MassEntityTemplateId` — that a Mass spawner would
  consume; it does not itself run Mass processors. The panicking, fleeing,
  bark-shouting behavioural half of `V5Crowd` is documented separately in
  [./perception-crowd-and-morality.md](./perception-crowd-and-morality.md); this
  page covers only the streaming/density seam.
- **Content is modelled as data, not cooked binaries.** `V5/ue/Plugins/` holds
  **29 `*.uplugin` GameFeature descriptors**, one per mode, and **zero have a
  `Source/` module** — they are content-and-manifest plugins (e.g.
  `Plugins/V5Mode_Urban_Online/Content/Data/urban_online_manifest.json`). A scan
  of the whole `V5/ue` tree finds **zero binary `.uasset`** and a single `.umap`
  (`Content/V5Core/Maps/BootMap.umap`). The crowd vocabulary is the exception
  that proves the model: a real **1.5 MB, 6,050-bark** `crowd_catalog.json` on
  disk (more below).

## Game modes — the `V5Modes` registry

### The mode definition and the 29-mode catalog

Everything begins with `FV5ModeDefinition` (`V5ModeTypes.h:59`): a `PluginName`
key, a `DisplayName`, an `EV5Cell`, an `EV5ModeNetworkModel`, an
`EV5ModeHUDSurface`, a `MaxPlayers`, and the boolean policy flags
(`bRequiresOnlineServices`, `bSupportsDedicatedServer`, `bSupportsListenServer`,
`bEditorOnly`, `bPersistentLobby`). It is richer than V4's five-field routing
record because the gate downstream reasons over the network model and the policy
flags rather than just opening a map. The catalog is authored in code by
`Catalog()` (`V5ModeSystems.cpp:38`), whose 29 `MakeMode(...)` rows
(`:41`–`:69`) span the seven cells: three Urban (HeistCity, StreetTriad,
Online), three Period (MadeMan, ViceSquad, InterrogationKit), two Frontier,
three Hunter, three Sci-Fi, plus the Steampunk, Mind-Palace/Cold-Cases,
Spec-Ops, Horde, Roguelike, four editor tools, Replay, Spectator, Training,
Bureau-HQ lobby, and Photo modes. `EnumerateEnabledGameFeaturePlugins`
(`V5ModeSystems.cpp:306`) intersects this catalog with a set of enabled plugin
names and splits the result into `EnabledModes` and `GatedModes` for a target —
the registry test pins the partition exactly (four enabled plugins on a
dedicated target yield 2 enabled / 2 gated).

### Build-target gating

The interesting logic is `GateKnownMode` (`V5ModeSystems.cpp:129`), a real
fail-closed policy switch over `EV5ModeBuildTarget` (`V5ModeTypes.h:19` — six
targets: `ShippingClient`, `DedicatedServer`, `ListenServer`, `Editor`,
`LANOffline`, `ArcadeCabinet`). An editor-only mode enables _only_ on the Editor
target. A dedicated server keeps only modes whose `bSupportsDedicatedServer` is
set. A listen target also admits offline/utility rulesets. `LANOffline` rejects
anything that `bRequiresOnlineServices`, or is `CompetitivePvP`, or is a
`SocialLobby`. And `ArcadeCabinet` cooks only the five curated cabinet modes
(`IsArcadeCabinetMode`, `:117`) and forces `bRequiresOnlineServices` false so a
cabinet runs offline. Each branch returns a populated `Reason` string, so a
gated mode explains itself. The spec drives the edges: an editor mode in a
shipping client returns disabled with a reason containing "Editor-only"; Hard
Vacuum is dedicated-eligible; a curated cabinet set yields exactly 5 enabled / 1
gated.

### Per-cell HUD routing

`BuildHUDRoute` (`V5ModeSystems.cpp:333`) maps `(EV5Cell, ActiveModePlugin)` to
an `FV5ModeHUDRoute` — a HUD surface, a `WidgetClassPath` soft path, primary/
secondary status labels, feature toggles (`bShowsNetworkPanel`,
`bShowsInvestigationNotebook`, `bShowsVehicleOrMountStatus`,
`bShowsSquadOrCrewStatus`), and a `RequiredLayers` list. It is genuinely
per-cell-and-per-mode: the Period cell forks ViceSquad ("Case"/"Evidence",
notebook + evidence tray) from MadeMan ("Family"/"Heat"), the Sci-Fi cell forks
the ship HUD ("Hull"/"Crew") from the squad HUD, and the Urban default surfaces
the network panel only for `V5Mode_Urban_Online`. The widget paths
(`/Game/V5/UI/HUD/WBP_HUD_*`) are soft references; no cooked widget binary backs
them in-tree, consistent with the content model above.

### Plugin discovery, lobby, and the esports/cabinet catalogs

Discovery is real engine I/O: `DiscoverGameFeaturePlugins`
(`V5ModeSystems.cpp:102`) walks `IPluginManager::Get().GetEnabledPlugins()` or
`GetDiscoveredPlugins()`, keeps only plugins whose name starts with `V5Mode_`
_and_ carry an enabled `GameFeatures` reference in their descriptor
(`IsV5GameFeaturePlugin`, `:86`), and returns a deterministically sorted `FName`
list. On top of the registry sit three authored catalogs with real validators:
`BuildLobbyState` (`:451`) mints the Bureau-HQ social lobby with seven cell
portals and four queue groups, clamping connected players to a 32-cap and
switching its primary action to "Retry online services" when services are
unhealthy; `ValidateArcadeCabinetProfile` (`:503`) is a ~30-assertion linter
over the 4K60 kiosk cabinet (five mode slots, coin-op disabled,
watchdog/service-menu gates); and `ValidateProStadiumTourCatalog` (`:577`)
checks the six-region, six-stop esports tour and its per-stop broadcast routes
(SRT/NDI/HLS/DASH, ≥14 camera feeds, a 15-second observer delay). These are
evergreen data validators, not gameplay runtimes, and their specs assert the
counts directly.

## World streaming — `V5Open`

### A real World-Partition streaming source

`UV5_Open_StreamingSubsystem` (`V5OpenStreamingSubsystem.h:10`) is a
`UWorldSubsystem` that _implements_ `IWorldPartitionStreamingSourceProvider`. On
`Initialize` it takes a dependency on `UWorldPartitionSubsystem` and registers
itself (`V5OpenStreamingSubsystem.cpp:6`, `:207`), so the engine's streamer
polls it every frame. Game code registers sources by actor or by location
(`RegisterStreamingActor` / `RegisterStreamingLocation`) tagged with an
`EV5OpenStreamingSourceKind` (`Player`, `Companion`, `MissionMarker`), and the
override `GetStreamingSources` (`:109`) translates each tracked source into a
real `FWorldPartitionStreamingSource`: it converts the metre load-radius to
centimetres (`× 100`), attaches an explicit-radius `FStreamingSourceShape`, sets
a debug colour by kind, forces 2D, and calls `UpdateHash`. Priority is policy,
not a constant: `ResolvePriority` (`:192`) gives the player `Highest`, a mission
marker `High` (`Highest` in the Sci-Fi cell, where objectives are the streaming
anchor), and a companion `Normal`. The high-density spec registers a player, a
mission marker, and 128 companions and asserts all 130 sources survive sorted —
player first, mission marker second, companion priority last — and that the
first emitted World-Partition source carries the `76800` cm radius. There is no
megabyte-budget HLOD pruner here the way V4 has one; V5 emits every registered
source and lets the engine's grid loading-range arbitrate.

### Mission pre-warm and the HLOD ladder

`GetPrewarmCellsForMissionStart` (`:167`) resolves a mission centre to the 256 m
partition lattice with `FloorToInt(center / PartitionCellSizeMeters)` and
returns the `(2r+1)²` block around it — radius 1 ⇒ the 9-cell 3×3 the spec
checks, including the centre cell. The HLOD model is `UV5_Open_HLODRules`
(`V5OpenHLODRules.cpp:3`): four layers on a contiguous distance ladder — HLOD0
`0–256 m`, HLOD1 `256–512 m`, HLOD2 `512–1000 m`, HLOD3 `1000–2000 m` flagged as
the silhouette. `ValidateRules` (`:46`) is a real authoring linter: it rejects
any cell size other than 256 m, demands exactly four layers indexed contiguously
with each band starting where the last ended, and requires HLOD3 to be the 2 km
silhouette layer. `GetRuleForDistance` (`:26`) resolves a distance to its band,
and the spec confirms 1500 m falls in HLOD3 and HLOD3 is the silhouette.

### Data layers: weather, time-of-day, era

Three `UTickableWorldSubsystem`s drive World-Partition data layers for real.
`UV5_Open_Weather` (`V5OpenWeather.cpp`) runs a seven-state machine
(`Dry`/`LightRain`/`HeavyRain`/`Snow`/`Fog`/`DustStorm`/`SolarFlare`) with timed
transitions; `GetCurrentGameplayModifiers` (`:99`) lerps four gameplay scalars —
visibility, traction, crowd-density, clue-legibility — across the transition,
and `ApplyWeatherDataLayer` (`:63`) activates the bound `UDataLayerAsset` and
unloads the rest through `UDataLayerManager::SetDataLayerRuntimeState`. The spec
proves the blend: heavy rain at its midpoint reports alpha `0.5`, reduced
visibility, and on completion a crowd-density scalar below `0.5` — weather
literally thins the streamed crowd. `UV5_Open_TimeOfDay`
(`V5OpenTimeOfDay.h:13`) models a `48 × 60`-second day (the spec advances it
half a cycle from 06:00 and lands on 18:00, and `IsNight` is
`hour ≥ 20 || hour < 5`). `UV5_Open_DataLayer_Era` (`V5OpenDataLayerEra.cpp:16`)
swaps the period-cell era bundle (1930s / 1947 / 1968) by activating exactly one
era's data layer — the engine seam for the period-authenticity world dressing.

### Population: traffic and pedestrian spawners

`V5Open` actually populates the streamed world. `UV5_Open_TrafficSpawner` and
`UV5_Open_PedSpawner` (`V5OpenPopulation.cpp`) hold per-cell densities and spawn
against them. `GetPedestrianDensityPerSquareKm` (`:193`) encodes the
architecture spec directly — Urban 250 people within a 100 m radius, Period 120,
Frontier 40 (settlements) / 6 (wilds), Hunter 60 / 10, Sci-Fi 80 / 30 — and
applies the period era scalars (1930s × 0.85, 1968 × 1.15).
`GetTrafficDensityPerSquareKm` (`:137`) does the same for vehicles (Urban
260/km², era-scaled Period 85/120/165, Frontier 18, Hunter 6, Sci-Fi 0 — no cars
in space). `CalculateCountFromDensity` (`:19`) converts a per-km² density and a
radius into an actor count via the real circle area `π·(r/1000)²`, clamped to a
`MaxActors` ceiling. `SpawnPopulation` (`:65`) then seeds an `FRandomStream`,
picks a weighted spawn class (`PickSpawnClass`), and calls
`UWorld::SpawnActor<AActor>` at a uniformly-sampled radial position;
`CullOutsideRadius` (`:100`) `Destroy`s anyone who drifts past the radius. The
density spec confirms Urban > Period > Frontier traffic, that 1968 pedestrians
outnumber 1930s, and — against a real game world — that spawning then culling
round-trips the active-actor count to the spawned count.

## Crowd density and the Mass seam — `V5Crowd`

The streaming side of crowds is the catalog plus the batch descriptor.
`UV5_Crowd_Catalog::ValidateCrowdCatalogJson` (`V5CrowdCatalog.cpp:248`) is a
strict schema-1 linter over `Content/V5Crowd/Data/crowd_catalog.json`: it
deserialises with `FJsonSerializer`, parses density profiles / body-language
libraries / bark lines, enforces unique ids, and rejects the set unless it has ≥
8 density profiles, **exactly six** era-bundle body-language libraries covering
six eras, ≥ 5000 ambient bark lines, and coverage of all five playable crowd
cells. The file on disk satisfies it for real — **1.5 MB, 8 density profiles, 6
libraries, and 6,050 authored barks** — so the "thousands of lines" claim is
bytes, not a template generator. `BuildPopulationBatch`
(`V5CrowdSystems.cpp:18`) is the bridge to streaming load: it scales a profile's
`PopulationWithin100m` by a console-throttle percent and a streaming-churn
scalar, then derives an even `EntitySpacingMeters` from the spawn-circle area
(`√(πr²/N)`) and stamps the authored `MassEntityTemplateId`. The catalog spec
reads back Urban's 250-NPC target and asserts the batch preserves 250 on desktop
but throttles below it on a console build. `GetPromotionRadius`
(`V5CrowdSystems.cpp:36`) holds the lightweight-to-pawn promotion thresholds the
architecture names — Named 18 m, Generic 30 m, Ambient 60 m — and
`EvaluatePromotion` (`:50`) promotes mission-critical or actively-interacting
NPCs unconditionally. The panic, flee, witness-report, and bark-selection logic
that rides on top of these entities lives in
[./perception-crowd-and-morality.md](./perception-crowd-and-morality.md).

## Procedural generation — `V5Procgen`

Six `BlueprintPure` generators fill the open world deterministically, each
seeding an `FRandomStream` and emitting a `StableFingerprint` string so a re-run
with the same seed is byte-identical (every spec asserts
`fingerprintA == fingerprintB` and `!= differentSeed`). The shared seed mixer
`CombineSeed` (`V5ProcgenSystems.cpp:44`) xors the inputs with the classic
spatial-hash constants (`73856093`, `19349663`, `83492791`) so distinct
cell/coord/pattern tuples decorrelate. The generators are: **side-quests**
(`GenerateSideQuest`, `:259`) over five authored patterns (talk-to-NPC, escort,
hunt, scavenge, race) with per-step jittered objective locations; **frontier
random encounters** (`BuildFrontierEncounterTemplates`, `:288`) — 320 weighted
templates across eight kinds and five region tags, with
`SelectFrontierEncounter` (`:317`) biasing Rescue encounters up for high-honor
players; **monster contracts** (`GenerateContract`, `:360`) drawing from 11
hand-authored archetypes with weakness tags, threat tiers, and crown rewards;
**loot tables** (`RollLoot`, `:392`) — a weighted-random walk
(`SelectWeightedLoot`, `:139`) over eight rarity-tiered entries; **city
decoration** (`GeneratePartitionDecoration`, `:409`) placing 36–60 cell-typed
prop instances per 256 m partition; and **galaxy detail**
(`GenerateSystemDetails`, `:437`) seeding mineral hotspots, anomalies, and
derelicts per Sci-Fi system. The spec coverage is concrete — 5 patterns, 320
encounters, 8 loot entries, ≥ 36 decoration instances, all three galaxy kinds —
and would fail instantly against a `Math::random` stub because it pins the
fingerprint, not just the shape.

## How a streamed open-world session fits together

The intended flow — assembled from the real functions above, even though no
single in-tree loop wires all four modules together per tick — looks like this:

```mermaid
flowchart TD
    Pick[Player picks mode] --> Reg[V5Modes registry]
    subgraph Modes [V5Modes]
        Reg --> Gate[GateKnownMode<br/>build-target policy] --> Hud[BuildHUDRoute<br/>per-cell HUD surface]
    end
    Gate -->|enabled mode + cell| Open
    subgraph Open [V5Open]
        Src[StreamingSubsystem<br/>IWorldPartitionStreamingSourceProvider] --> WP[(UWorldPartitionSubsystem)]
        Pre[GetPrewarmCellsForMissionStart<br/>3x3 of 256m cells] --> WP
        Layers[Weather / TimeOfDay / Era<br/>UDataLayerManager] --> WP
        WP --> Pop[Traffic + Ped spawners<br/>density to UWorld::SpawnActor]
    end
    Pop -->|density profile| Crowd
    subgraph Crowd [V5Crowd]
        Batch[BuildPopulationBatch<br/>lightweight count + spacing] --> Promo[GetPromotionRadius<br/>18 / 30 / 60 m]
    end
    Seed[Run seed] --> Procgen
    subgraph Procgen [V5Procgen]
        Gen[6 FRandomStream generators<br/>StableFingerprint] --> Content[quests / encounters / loot / decoration]
    end
    Content -.fills streamed cells.-> Pop
    Layers -.crowd-density scalar.-> Batch
```

## Related

- [./perception-crowd-and-morality.md](./perception-crowd-and-morality.md) — the
  panic propagation, witness reports, barks, and morality that the streamed
  crowd this page spawns then runs on
- [./netcode-authority-and-determinism.md](./netcode-authority-and-determinism.md)
  — the server authority and lockstep determinism behind the online modes the
  registry gates and the seeds it shares
- [./telemetry-build-and-data.md](./telemetry-build-and-data.md) — the cook,
  arcade-cabinet packaging, and content-as-data accounting (0 binary `.uasset`,
  29 `.uplugin`) these systems assume
- [../V5_ARCHITECTURE.md](../V5_ARCHITECTURE.md) — the orientation hub
