ue-world-level-streaming
Use this skill when working with World Partition, level streaming, level travel, OpenLevel, ServerTravel, data layer, world subsystem, level instance, sub-level, seamless travel, open world, or HLOD. See references/streaming-patterns.md for configuration patterns by game type.
What this skill does
# UE World & Level Streaming
You are an expert in Unreal Engine's world management and level streaming systems.
---
## Context
Read `.agents/ue-project-context.md` before advising. Pay attention to:
- **Engine version** — World Partition is UE5 only; sub-level streaming works in both UE4 and UE5.
- **Build targets** — Dedicated server has no rendering-driven streaming; streaming must be server-safe.
- **World size** — Determines whether World Partition or manual sub-level streaming is appropriate.
- **Multiplayer** — Seamless travel requirements and per-player streaming radius.
---
## Information to Gather
Before recommending a streaming approach, confirm:
1. **World size and type**: Is this an open world (World Partition), a set of discrete levels, or a hub-and-spoke map?
2. **Multiplayer**: Are you running a dedicated server? Are per-player streaming radii needed?
3. **Streaming control**: Does gameplay code need to control load/unload explicitly, or should proximity drive it?
4. **Level travel**: Non-seamless (lobby flows), seamless (multiplayer round transitions), or no travel?
5. **Persistent data**: What must survive a level transition — player state, inventory, session state?
---
## World Partition (UE5)
### Enabling World Partition
Enable via the Level menu: **World -> World Partition -> Convert Level**. Once enabled, all actors in the level are managed by World Partition's grid. The level can no longer have traditional sub-levels. Use **One File Per Actor (OFPA)** for collaborative editing: each actor is saved as its own `.uasset` under `__ExternalActors__`.
### Runtime Data Layers
Data layers replace the old sub-level toggle pattern. A runtime data layer can be loaded/unloaded at runtime without traveling to a new map.
```cpp
// MyGameMode.cpp
#include "WorldPartition/DataLayer/DataLayerManager.h"
void AMyGameMode::ActivateDungeonDataLayer()
{
UDataLayerManager* DLMgr = UDataLayerManager::GetDataLayerManager(GetWorld());
if (!DLMgr) return;
// Get by asset reference (set up in editor as a UDataLayerAsset)
UDataLayerAsset* DungeonLayer = DungeonDataLayerAsset.LoadSynchronous();
DLMgr->SetDataLayerRuntimeState(DungeonLayer, EDataLayerRuntimeState::Activated);
}
void AMyGameMode::DeactivateDungeonDataLayer()
{
UDataLayerManager* DLMgr = UDataLayerManager::GetDataLayerManager(GetWorld());
if (!DLMgr) return;
UDataLayerAsset* DungeonLayer = DungeonDataLayerAsset.LoadSynchronous();
DLMgr->SetDataLayerRuntimeState(DungeonLayer, EDataLayerRuntimeState::Unloaded);
}
```
**Data layer states:**
- `Unloaded` — not loaded, not visible.
- `Loaded` — loaded into memory, not visible (pre-warming).
- `Activated` — loaded and visible (fully active).
### Streaming Sources
Each player controller is a streaming source by default. For custom sources (cinematic cameras, AI directors), implement `IWorldPartitionStreamingSourceProvider`.
### HLOD
HLOD provides distant merged-mesh representations of World Partition cells. Configure HLOD layers in the World Partition editor; build before shipping via **Build -> Build World Partition HLODs**. Without HLOD, content beyond the streaming radius is simply absent.
### Converting Sub-Levels to World Partition
Use **Tools -> World Partition -> Convert Level**. Actors migrate into the persistent level under WP management. Audit cross-level references beforehand — hard references to converted actors become invalid.
### World Partition and Multiplayer
In a multiplayer session, each player controller acts as a streaming source with a configurable radius. The server streams based on server-side sources; clients receive visibility updates via `AServerStreamingLevelsVisibility`. On dedicated servers, rendering-based streaming does not apply — streaming is driven by server-side sources only.
Streaming radius is configured per-partition in the World Partition editor UI (`LoadingRange` on `URuntimePartition`), not via ini.
---
## Level Streaming (Manual Sub-Levels)
### ULevelStreaming State Machine
From `LevelStreaming.h`, the full state sequence is:
```
Removed -> Unloaded -> Loading -> LoadedNotVisible -> MakingVisible -> LoadedVisible -> MakingInvisible -> LoadedNotVisible
|
FailedToLoad (check logs; level asset missing or corrupt)
```
Query state with:
```cpp
ULevelStreaming* StreamingLevel = /* ... */;
ELevelStreamingState State = StreamingLevel->GetLevelStreamingState();
switch (State)
{
case ELevelStreamingState::Unloaded: /* not in memory */ break;
case ELevelStreamingState::Loading: /* async load in progress */ break;
case ELevelStreamingState::LoadedNotVisible: /* in memory, not rendered */ break;
case ELevelStreamingState::MakingVisible: /* adding to world */ break;
case ELevelStreamingState::LoadedVisible: /* fully active */ break;
case ELevelStreamingState::MakingInvisible: /* removing from rendering */ break;
case ELevelStreamingState::FailedToLoad: /* check logs */ break;
}
```
### UGameplayStatics: LoadStreamLevel / UnloadStreamLevel
For Blueprint-friendly async streaming with latent actions (from `GameplayStatics.h`):
```cpp
// MyActor.cpp — async load using FLatentActionInfo
#include "Kismet/GameplayStatics.h"
void AMyActor::StreamInRoom(FName LevelName)
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnRoomLoaded");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetUniqueID();
UGameplayStatics::LoadStreamLevel(
this, // WorldContextObject
LevelName, // e.g., FName("Room_01")
true, // bMakeVisibleAfterLoad
false, // bShouldBlockOnLoad — keep false for async
LatentInfo
);
}
UFUNCTION()
void AMyActor::OnRoomLoaded()
{
// Room is now loaded and visible
}
void AMyActor::StreamOutRoom(FName LevelName)
{
FLatentActionInfo LatentInfo;
LatentInfo.CallbackTarget = this;
LatentInfo.ExecutionFunction = FName("OnRoomUnloaded");
LatentInfo.Linkage = 0;
LatentInfo.UUID = GetUniqueID() + 1;
UGameplayStatics::UnloadStreamLevel(
this,
LevelName,
LatentInfo,
false // bShouldBlockOnUnload
);
}
```
For soft object pointers (preferred for packaging safety), use `LoadStreamLevelBySoftObjectPtr` with the same arguments.
### ULevelStreamingDynamic: Runtime Level Instances
Use `ULevelStreamingDynamic::LoadLevelInstance` to load the same level package multiple times at different transforms — for procedural dungeons, modular buildings, or instanced rooms (from `LevelStreamingDynamic.h`):
```cpp
#include "Engine/LevelStreamingDynamic.h"
void AMyDungeonGenerator::SpawnRoom(FVector Location, FRotator Rotation)
{
bool bSuccess = false;
ULevelStreamingDynamic* StreamingLevel = ULevelStreamingDynamic::LoadLevelInstance(
this, // WorldContextObject
TEXT("/Game/Levels/Room_Corridor"), // LongPackageName — full path
Location,
Rotation,
bSuccess
);
if (bSuccess && StreamingLevel)
{
// Bind to delegate to know when visible
StreamingLevel->OnLevelShown.AddDynamic(this, &AMyDungeonGenerator::OnRoomShown);
StreamingLevel->OnLevelHidden.AddDynamic(this, &AMyDungeonGenerator::OnRoomHidden);
LoadedRooms.Add(StreamingLevel);
}
}
void AMyDungeonGenerator::UnloadRoom(ULevelStreamingDynamic* StreamingLevel)
{
if (StreamingLevel)
{
StreamingLevel->SetShouldBeLoaded(false);
StreamingLevel->SetShouldBeVisible(false);
StreamingLevel->SetIsRequestingUnloadAndRemoval(true);
}
}
```
For networking: use `OptionalLevelNameOverride` to give all clients and server the same package name for a given instance. Without this, names are auto-generated uniquely per process and will not match aRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.