claude-code-arcane 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +104 -55
  3. package/agents/career/career-strategist.md +88 -0
  4. package/agents/career/cv-reviewer.md +81 -0
  5. package/agents/career/hiring-manager.md +68 -0
  6. package/agents/career/mock-interviewer.md +76 -0
  7. package/agents/freelance/client-screener.md +84 -0
  8. package/agents/freelance/discovery-call.md +65 -0
  9. package/agents/freelance/pipeline-strategist.md +99 -0
  10. package/agents/freelance/proposal-reviewer.md +82 -0
  11. package/agents/game/ue-blueprint-specialist.md +125 -0
  12. package/agents/game/ue-gas-specialist.md +133 -0
  13. package/agents/game/ue-umg-specialist.md +131 -0
  14. package/dist/cli.js +57 -13
  15. package/docs/RELEASE-SETUP.md +1 -1
  16. package/docs/SKILLS-CATALOG.md +67 -4
  17. package/docs/USER-GUIDE.md +77 -72
  18. package/docs/agent-hierarchy.md +21 -1
  19. package/docs/presentations/build_arcane_deck.py +2 -2
  20. package/docs/presentations/build_arcane_deck_12.py +2 -2
  21. package/hooks/detect-unreal.sh +90 -0
  22. package/package.json +2 -2
  23. package/profiles/freelance.yaml +43 -0
  24. package/profiles/job-hunt.yaml +5 -2
  25. package/profiles/unreal-dev.yaml +97 -0
  26. package/rules/drafter-reviewer.md +16 -3
  27. package/rules/freelance-guardrails.md +53 -0
  28. package/rules/gamedev/unreal-code.md +125 -0
  29. package/skills/career-registry/SKILL.md +95 -0
  30. package/skills/career-registry/scripts/career_registry.py +488 -0
  31. package/skills/client-screen/SKILL.md +83 -0
  32. package/skills/client-screen/references/red-flags.md +95 -0
  33. package/skills/cover-letter/SKILL.md +4 -3
  34. package/skills/cv-tailor/SKILL.md +11 -2
  35. package/skills/freelance-deliver/SKILL.md +90 -0
  36. package/skills/freelance-hunt/SKILL.md +126 -0
  37. package/skills/freelance-hunt/references/templates/Connects.md +39 -0
  38. package/skills/freelance-hunt/references/templates/Contrato.md +60 -0
  39. package/skills/freelance-hunt/references/templates/Propuesta.md +66 -0
  40. package/skills/freelance-kickoff/SKILL.md +94 -0
  41. package/skills/freelance-pipeline/SKILL.md +109 -0
  42. package/skills/freelance-pipeline/scripts/import_history.py +425 -0
  43. package/skills/freelance-profile/SKILL.md +93 -0
  44. package/skills/freelance-proposal/SKILL.md +112 -0
  45. package/skills/freelance-scan/SKILL.md +196 -0
  46. package/skills/freelance-scan/references/dedup.md +35 -0
  47. package/skills/freelance-scan/references/platforms.md +167 -0
  48. package/skills/freelance-scan/scripts/freelance_search.py +1077 -0
  49. package/skills/install-mcp/SKILL.md +135 -46
  50. package/skills/install-mcp/references/unreal-operations.md +55 -0
  51. package/skills/interview-prep/SKILL.md +11 -3
  52. package/skills/job-aplicar/SKILL.md +5 -1
  53. package/skills/job-hunt/SKILL.md +17 -0
  54. package/skills/job-outcome/SKILL.md +15 -1
  55. package/skills/job-upskill/SKILL.md +10 -2
  56. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  57. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  58. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  59. package/skills/ue-ai-navigation/SKILL.md +526 -0
  60. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  61. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  62. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  63. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  64. package/skills/ue-animation-system/SKILL.md +521 -0
  65. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  66. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  67. package/skills/ue-async-threading/SKILL.md +433 -0
  68. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  69. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  70. package/skills/ue-audio-system/SKILL.md +527 -0
  71. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  72. package/skills/ue-blueprints/SKILL.md +134 -0
  73. package/skills/ue-blueprints/references/communication.md +82 -0
  74. package/skills/ue-character-movement/SKILL.md +475 -0
  75. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  76. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  77. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  78. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  79. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  80. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  81. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  82. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  83. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  84. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  85. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  86. package/skills/ue-editor-tools/SKILL.md +523 -0
  87. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  88. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  89. package/skills/ue-game-features/SKILL.md +477 -0
  90. package/skills/ue-game-features/references/experience-system.md +222 -0
  91. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  92. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  93. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  94. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  95. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  96. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  97. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  98. package/skills/ue-input-system/SKILL.md +394 -0
  99. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  100. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  101. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  102. package/skills/ue-mass-entity/SKILL.md +476 -0
  103. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  104. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  105. package/skills/ue-materials-rendering/SKILL.md +526 -0
  106. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  107. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  108. package/skills/ue-module-build-system/SKILL.md +487 -0
  109. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  110. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  111. package/skills/ue-naming-conventions/SKILL.md +142 -0
  112. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  113. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  114. package/skills/ue-networking-replication/SKILL.md +452 -0
  115. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  116. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  117. package/skills/ue-niagara-effects/SKILL.md +531 -0
  118. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  119. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  120. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  121. package/skills/ue-packaging/SKILL.md +116 -0
  122. package/skills/ue-physics-collision/SKILL.md +423 -0
  123. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  124. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  125. package/skills/ue-procedural-generation/SKILL.md +505 -0
  126. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  127. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  128. package/skills/ue-project-context/SKILL.md +305 -0
  129. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  130. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  131. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  132. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  133. package/skills/ue-state-trees/SKILL.md +489 -0
  134. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  135. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  136. package/skills/ue-testing-debugging/SKILL.md +520 -0
  137. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  138. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  139. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  140. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  141. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  142. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  143. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  144. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,414 @@
1
+ # Level Streaming Configuration Patterns
2
+
3
+ Reference patterns for common game types. Choose the pattern that matches your project's world structure and adapt the specifics to your scale and multiplayer requirements.
4
+
5
+ ---
6
+
7
+ ## Pattern 1: Open World with World Partition (UE5)
8
+
9
+ **Best for:** Large continuous worlds — survival games, open-world RPGs, exploration games.
10
+
11
+ ### Configuration
12
+
13
+ Enable World Partition on the persistent level. Every actor is managed by the WP grid. Sub-levels are not used.
14
+
15
+ Streaming radius is configured per-partition in the World Partition editor UI (`LoadingRange` on `URuntimePartition`). World Partition streaming is enabled per-world in the editor, not via ini.
16
+
17
+ ### Data Layers for Dynamic Content
18
+
19
+ ```cpp
20
+ // Assign actors to data layers in editor (details panel: Data Layers).
21
+ // At runtime, activate/deactivate via UDataLayerManager (UE 5.3+).
22
+ // NOTE: UDataLayerSubsystem is deprecated since UE 5.3 — use UDataLayerManager instead.
23
+
24
+ // Example: interior content (caves, buildings) only loads when player is near
25
+ #include "WorldPartition/DataLayer/DataLayerManager.h"
26
+ UDataLayerManager* DLMgr = UDataLayerManager::GetDataLayerManager(GetWorld());
27
+
28
+ // SetDataLayerRuntimeState takes UDataLayerAsset* (asset reference set up in editor)
29
+ UDataLayerAsset* InteriorLayerAsset = /* UPROPERTY asset reference */;
30
+
31
+ // Activate — loads and makes visible
32
+ DLMgr->SetDataLayerRuntimeState(InteriorLayerAsset, EDataLayerRuntimeState::Activated);
33
+
34
+ // Pre-load without making visible (background warm)
35
+ DLMgr->SetDataLayerRuntimeState(InteriorLayerAsset, EDataLayerRuntimeState::Loaded);
36
+
37
+ // Unload completely
38
+ DLMgr->SetDataLayerRuntimeState(InteriorLayerAsset, EDataLayerRuntimeState::Unloaded);
39
+ ```
40
+
41
+ ### Streaming Radius per Player (Multiplayer)
42
+
43
+ World Partition automatically uses each player controller as a streaming source. Each controller can have a different runtime cell size. Override `IWorldPartitionStreamingSourceProvider` for non-player streaming sources (e.g., AI directors, cinematic cameras).
44
+
45
+ ### HLOD Setup
46
+
47
+ 1. Open World Partition editor panel.
48
+ 2. Add HLOD layers: one per LOD tier (e.g., far, medium).
49
+ 3. Set cell size to match grid (e.g., 128m cells = 12800 units).
50
+ 4. Build HLODs before shipping: **Build -> Build World Partition HLODs**.
51
+
52
+ ### Anti-Patterns for This Setup
53
+
54
+ - Do not use streaming volumes — they have no effect in World Partition.
55
+ - Do not add actors to persistent level outside of World Partition (they will always be loaded).
56
+ - Do not use `UGameplayStatics::LoadStreamLevel` — there are no named sub-levels to target.
57
+
58
+ ---
59
+
60
+ ## Pattern 2: Hub-and-Spoke with Manual Sub-Level Streaming
61
+
62
+ **Best for:** Games with discrete zones (MMO zones, dungeon crawlers, hub worlds with portal travel).
63
+
64
+ ### Persistent Level
65
+
66
+ The persistent level contains: GameMode, GameState, player spawn points, UI actors, and global managers. It never unloads during a session.
67
+
68
+ ### Sub-Levels Setup
69
+
70
+ Each zone is a separate `.umap` added to the persistent level's streaming list in the editor. Zones are loaded on demand.
71
+
72
+ ```cpp
73
+ // MyZoneManager.h — UWorldSubsystem for zone state tracking
74
+ UCLASS()
75
+ class UMyZoneManager : public UWorldSubsystem
76
+ {
77
+ GENERATED_BODY()
78
+
79
+ public:
80
+ void LoadZone(FName ZoneLevelName);
81
+ void UnloadZone(FName ZoneLevelName);
82
+ bool IsZoneLoaded(FName ZoneLevelName) const;
83
+
84
+ private:
85
+ TSet<FName> LoadedZones;
86
+
87
+ UFUNCTION()
88
+ void OnZoneLoaded() { /* update LoadedZones */ }
89
+ };
90
+ ```
91
+
92
+ ```cpp
93
+ // MyZoneManager.cpp
94
+ void UMyZoneManager::LoadZone(FName ZoneLevelName)
95
+ {
96
+ FLatentActionInfo LatentInfo;
97
+ LatentInfo.CallbackTarget = this;
98
+ LatentInfo.ExecutionFunction = FName("OnZoneLoaded");
99
+ LatentInfo.Linkage = 0;
100
+ LatentInfo.UUID = GetTypeHash(ZoneLevelName);
101
+
102
+ UGameplayStatics::LoadStreamLevel(this, ZoneLevelName, true, false, LatentInfo);
103
+ }
104
+
105
+ void UMyZoneManager::UnloadZone(FName ZoneLevelName)
106
+ {
107
+ FLatentActionInfo LatentInfo;
108
+ LatentInfo.CallbackTarget = this;
109
+ LatentInfo.ExecutionFunction = FName("OnZoneUnloaded");
110
+ LatentInfo.Linkage = 0;
111
+ LatentInfo.UUID = GetTypeHash(ZoneLevelName) + 1;
112
+
113
+ UGameplayStatics::UnloadStreamLevel(this, ZoneLevelName, LatentInfo, false);
114
+ LoadedZones.Remove(ZoneLevelName);
115
+ }
116
+ ```
117
+
118
+ ### Pre-Loading Adjacent Zones
119
+
120
+ Load neighbor zones into `LoadedNotVisible` state so they are in memory before the player arrives:
121
+
122
+ ```cpp
123
+ void UMyZoneManager::PreloadAdjacentZone(FName ZoneLevelName)
124
+ {
125
+ const TArray<ULevelStreaming*>& Levels = GetWorld()->GetStreamingLevels();
126
+ for (ULevelStreaming* Level : Levels)
127
+ {
128
+ if (Level->GetWorldAssetPackageFName() == ZoneLevelName)
129
+ {
130
+ Level->SetShouldBeLoaded(true);
131
+ Level->SetShouldBeVisible(false); // Load but keep invisible
132
+ // Changes are picked up by the streaming system each frame automatically
133
+ return;
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### Loading Screen Handoff
140
+
141
+ Before loading screen dismiss, wait for the target zone to reach `LoadedVisible`:
142
+
143
+ ```cpp
144
+ void UMyHUD::WaitForZoneVisible(FName ZoneLevelName)
145
+ {
146
+ GetWorld()->GetTimerManager().SetTimer(
147
+ PollTimerHandle,
148
+ [this, ZoneLevelName]()
149
+ {
150
+ for (ULevelStreaming* Level : GetWorld()->GetStreamingLevels())
151
+ {
152
+ if (Level->GetWorldAssetPackageFName() == ZoneLevelName &&
153
+ Level->GetLevelStreamingState() == ELevelStreamingState::LoadedVisible)
154
+ {
155
+ DismissLoadingScreen();
156
+ GetWorld()->GetTimerManager().ClearTimer(PollTimerHandle);
157
+ return;
158
+ }
159
+ }
160
+ },
161
+ 0.1f, // poll every 100ms
162
+ true // looping
163
+ );
164
+ }
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Pattern 3: Procedural / Instanced Level Streaming
170
+
171
+ **Best for:** Roguelikes, procedural dungeons, instanced arenas, modular buildings loaded at runtime.
172
+
173
+ ### Core Pattern: ULevelStreamingDynamic
174
+
175
+ ```cpp
176
+ // ProcDungeonGenerator.h
177
+ UCLASS()
178
+ class AProcDungeonGenerator : public AActor
179
+ {
180
+ GENERATED_BODY()
181
+
182
+ public:
183
+ // Room template level to instance (set in editor)
184
+ UPROPERTY(EditDefaultsOnly)
185
+ TSoftObjectPtr<UWorld> RoomTemplate;
186
+
187
+ void SpawnRoom(FTransform RoomTransform, FString InstanceName);
188
+ void DespawnRoom(FString InstanceName);
189
+
190
+ private:
191
+ UPROPERTY()
192
+ TMap<FString, TObjectPtr<ULevelStreamingDynamic>> SpawnedRooms;
193
+
194
+ UFUNCTION()
195
+ void OnRoomVisible();
196
+ };
197
+ ```
198
+
199
+ ```cpp
200
+ // ProcDungeonGenerator.cpp
201
+ void AProcDungeonGenerator::SpawnRoom(FTransform RoomTransform, FString InstanceName)
202
+ {
203
+ ULevelStreamingDynamic::FLoadLevelInstanceParams Params(
204
+ GetWorld(),
205
+ RoomTemplate.GetLongPackageName(),
206
+ RoomTransform
207
+ );
208
+ Params.OptionalLevelNameOverride = &InstanceName; // Deterministic name for net sync
209
+ Params.bInitiallyVisible = true;
210
+
211
+ bool bSuccess = false;
212
+ ULevelStreamingDynamic* Level = ULevelStreamingDynamic::LoadLevelInstance(Params, bSuccess);
213
+
214
+ if (bSuccess && Level)
215
+ {
216
+ Level->OnLevelShown.AddDynamic(this, &AProcDungeonGenerator::OnRoomVisible);
217
+ SpawnedRooms.Add(InstanceName, Level);
218
+ }
219
+ }
220
+
221
+ void AProcDungeonGenerator::DespawnRoom(FString InstanceName)
222
+ {
223
+ if (ULevelStreamingDynamic** Level = SpawnedRooms.Find(InstanceName))
224
+ {
225
+ (*Level)->SetShouldBeLoaded(false);
226
+ (*Level)->SetShouldBeVisible(false);
227
+ (*Level)->SetIsRequestingUnloadAndRemoval(true);
228
+ SpawnedRooms.Remove(InstanceName);
229
+ }
230
+ }
231
+ ```
232
+
233
+ ### Multiplayer: Replicating Instance Names
234
+
235
+ The server spawns room instances with deterministic names (e.g., "Room_0001", "Room_0002"). It replicates these names to clients via a replicated array on GameState. Clients call `LoadLevelInstance` with the same `OptionalLevelNameOverride`. The names must match exactly — without this, clients and server reference different package names and streaming breaks.
236
+
237
+ ```cpp
238
+ // MyGameState.h
239
+ UPROPERTY(ReplicatedUsing=OnRep_SpawnedRooms)
240
+ TArray<FString> SpawnedRoomNames;
241
+
242
+ UFUNCTION()
243
+ void OnRep_SpawnedRooms();
244
+
245
+ // MyGameState.cpp — client side
246
+ void AMyGameState::OnRep_SpawnedRooms()
247
+ {
248
+ for (const FString& RoomName : SpawnedRoomNames)
249
+ {
250
+ // Load each room with the replicated name as the override
251
+ // so the package name matches what the server has
252
+ DungeonGenerator->SpawnRoom(GetRoomTransform(RoomName), RoomName);
253
+ }
254
+ }
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Pattern 4: Linear Level Sequence (Chapter / Act Structure)
260
+
261
+ **Best for:** Narrative games, linear action games with distinct chapters or missions.
262
+
263
+ ### Approach
264
+
265
+ Each chapter is a separate `.umap`. A small persistent level holds global actors. Travel between chapters uses seamless travel (multiplayer) or `OpenLevel` (single-player).
266
+
267
+ ### Single-Player: OpenLevel with GameInstance State
268
+
269
+ ```cpp
270
+ // Save chapter progress to GameInstance before traveling
271
+ void AMyGameMode::TravelToChapter(FName ChapterMapName)
272
+ {
273
+ UMyGameInstance* GI = GetGameInstance<UMyGameInstance>();
274
+ if (GI)
275
+ {
276
+ GI->LastCompletedChapter = CurrentChapter;
277
+ GI->PlayerInventory = CollectPlayerInventory();
278
+ }
279
+
280
+ UGameplayStatics::OpenLevel(this, ChapterMapName, true);
281
+ }
282
+ ```
283
+
284
+ ### Multiplayer: Seamless Travel
285
+
286
+ ```ini
287
+ ; DefaultEngine.ini
288
+ [/Script/Engine.GameMapsSettings]
289
+ TransitionMap=/Game/Maps/Transition_Loading
290
+ ```
291
+
292
+ ```cpp
293
+ // MyGameMode.h
294
+ uint32 bUseSeamlessTravel : 1; // set to 1 in constructor
295
+
296
+ // MyGameMode.cpp
297
+ void AMyGameMode::TravelToChapter(const FString& ChapterURL)
298
+ {
299
+ // Server initiates; clients follow automatically
300
+ GetWorld()->ServerTravel(ChapterURL);
301
+ }
302
+
303
+ void AMyGameMode::GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList)
304
+ {
305
+ Super::GetSeamlessTravelActorList(bToTransition, ActorList);
306
+
307
+ if (!bToTransition)
308
+ {
309
+ // Carry GameState into the destination
310
+ ActorList.Add(GameState);
311
+ }
312
+ }
313
+
314
+ void AMyGameMode::HandleSeamlessTravelPlayer(AController*& C)
315
+ {
316
+ Super::HandleSeamlessTravelPlayer(C);
317
+ // Restore character state from GameInstance or GameState
318
+ RestorePlayerState(C);
319
+ }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Pattern 5: Streaming Volume–Driven Interior Loading
325
+
326
+ **Best for:** Open world games with buildings or interiors that stream in as the player approaches.
327
+
328
+ ### Setup
329
+
330
+ 1. Create a sub-level per interior (e.g., `L_Building_Interior_01`).
331
+ 2. Place an `ALevelStreamingVolume` in the persistent level surrounding the building exterior.
332
+ 3. Set `StreamingUsage = SVB_LoadingAndVisibility` on the volume.
333
+ 4. Assign the sub-level to the volume via the streaming level's `EditorStreamingVolumes` array (set in editor Details panel or via Levels panel).
334
+ 5. Set `MinTimeBetweenVolumeUnloadRequests = 5.0` seconds on the sub-level to prevent unload flicker when the player stands near the volume boundary.
335
+
336
+ ### Disabling Volume Control at Runtime (Cutscene or Boss Arena)
337
+
338
+ ```cpp
339
+ void AMyBossRoomTrigger::BeginPlay()
340
+ {
341
+ Super::BeginPlay();
342
+ // Find the streaming level and disable volume-based streaming
343
+ for (ULevelStreaming* Level : GetWorld()->GetStreamingLevels())
344
+ {
345
+ if (Level->GetWorldAssetPackageFName() == FName("/Game/Levels/L_BossArena"))
346
+ {
347
+ // Disable volume control — we'll manage load state from code
348
+ Level->bDisableDistanceStreaming = true;
349
+ return;
350
+ }
351
+ }
352
+ }
353
+
354
+ void AMyBossRoomTrigger::NotifyActorBeginOverlap(AActor* OtherActor)
355
+ {
356
+ if (OtherActor->IsA<APlayerCharacter>())
357
+ {
358
+ // Manually load and show
359
+ for (ULevelStreaming* Level : GetWorld()->GetStreamingLevels())
360
+ {
361
+ if (Level->GetWorldAssetPackageFName() == FName("/Game/Levels/L_BossArena"))
362
+ {
363
+ Level->SetShouldBeLoaded(true);
364
+ Level->SetShouldBeVisible(true);
365
+ // Changes are picked up by the streaming system each frame automatically
366
+ }
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ---
373
+
374
+ ## Dedicated Server Streaming Considerations
375
+
376
+ On a dedicated server, there is no rendering pipeline. Streaming must be entirely logic-driven.
377
+
378
+ - **World Partition**: streaming sources on the server must be explicit. Player controller positions are used by default if `APlayerController` is registered as a source.
379
+ - **Sub-levels**: call `SetShouldBeLoaded` and `SetShouldBeVisible` explicitly. Volume-based streaming is disabled (no camera player controller overlap in server-only mode without clients).
380
+ - **Avoid `bShouldBlockOnLoad`** on the server unless behind a map-change sequence — blocking the server stalls all clients.
381
+
382
+ ```cpp
383
+ // Server-side: manually drive zone loading based on player positions
384
+ // Inherits UTickableWorldSubsystem (UWorldSubsystem has no Tick)
385
+ void UServerZoneSubsystem::Tick(float DeltaTime)
386
+ {
387
+ if (GetWorld()->GetNetMode() != NM_DedicatedServer) return;
388
+
389
+ for (APlayerController* PC : TActorRange<APlayerController>(GetWorld()))
390
+ {
391
+ FVector PlayerPos = PC->GetPawn() ? PC->GetPawn()->GetActorLocation() : FVector::ZeroVector;
392
+ FName TargetZone = DetermineZoneForPosition(PlayerPos);
393
+ if (!LoadedZones.Contains(TargetZone))
394
+ {
395
+ LoadZone(TargetZone);
396
+ }
397
+ }
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Performance Benchmarks and Budget Guidelines
404
+
405
+ | Scenario | Recommended Cell/Zone Size | Max Simultaneous Loaded Levels |
406
+ |---|---|---|
407
+ | Open world (WP) | 128m–256m cells | N/A (grid-based) |
408
+ | Hub-and-spoke zones | Per zone (no fixed size) | 2–3 (current + neighbors) |
409
+ | Procedural rooms | Per room (10m–50m) | 10–30 depending on actor counts |
410
+ | Interior streaming | Per building | 3–5 around player |
411
+
412
+ **Actor counts per cell (World Partition):** Keep under 1000 dynamic actors per cell. Static meshes are merged by HLOD and do not count against this limit in distant cells.
413
+
414
+ **Loading budget per frame:** Level streaming operations are spread across frames. `bShouldBlockOnLoad` forces all operations into a single frame — only acceptable during loading screens. Async loading typically completes in 0.5–5 seconds depending on level size and disk speed.
@@ -0,0 +1,107 @@
1
+ # Unreal Project Context: [Project Name]
2
+
3
+ > Lives at `docs/unreal/project-context.md`. Every `ue-*` skill reads this before acting, so
4
+ > keeping it accurate is what stops the advice from being generic. Generate or refresh it with
5
+ > `/ue-project-context`.
6
+
7
+ ## Document Status
8
+ - **Version**: 1.0
9
+ - **Last Updated**: [Date]
10
+ - **Maintainer**: [Agent/Person]
11
+ - **Drafted from**: [codebase scan / interview / both]
12
+
13
+ ## 1. Engine & Project Overview
14
+
15
+ | Field | Value |
16
+ |-------|-------|
17
+ | **Project name** | [Name] |
18
+ | **One-line description** | [What this is] |
19
+ | **Engine version** | [e.g. 5.4.4] |
20
+ | **Build kind** | [launcher build / source build] — decides where `.mcp.json` is written |
21
+ | **Project type** | [game / simulation / visualization / tool / plugin] |
22
+ | **Genre or domain** | [e.g. first-person shooter, architectural viz, training sim] |
23
+ | **Target platforms** | [Windows, Mac, Linux, PS5, Xbox, iOS, Android, VR] |
24
+
25
+ ## 2. Module Structure
26
+
27
+ | Module | Type | Primary? | Public deps | Private deps |
28
+ |--------|------|----------|-------------|--------------|
29
+ | [Name] | [Runtime / Editor / Developer / ThirdParty] | [yes/no] | [...] | [...] |
30
+
31
+ - **Modules under active development**: [...]
32
+ - **Modules stable or locked**: [...]
33
+ - **Standalone plugins or shared libraries**: [...]
34
+
35
+ ## 3. Plugin Dependencies
36
+
37
+ | Plugin | Source | Critical to gameplay? | Notes |
38
+ |--------|--------|-----------------------|-------|
39
+ | [e.g. GameplayAbilities] | [engine / Fab / in-house] | [yes/no] | [licence restrictions, version pins] |
40
+
41
+ Engine plugins to record if enabled: GameplayAbilities, EnhancedInput, CommonUI, Niagara, PCG,
42
+ MetaSounds, Chaos, OnlineSubsystem, MassEntity, StateTree, ModelContextProtocol + AllToolsets.
43
+
44
+ ## 4. Coding & Asset Conventions
45
+
46
+ ### Asset naming
47
+
48
+ - **Pattern**: [`Prefix_BaseAssetName_Variant_Suffix`, or the project's own]
49
+ - **Static Mesh prefix**: [`SM_` or `S_` — the two common conventions disagree; pick one]
50
+ - **Structs / enums authored in Blueprint**: [`F`/`E` like C++, or `F_`/`E_` to distinguish]
51
+ - **Texture packing order**: [e.g. `_RAM` = Roughness/AO/Metallic in RGB]
52
+ - **Prefix collisions resolved**: [e.g. `Submix_` vs `SM_`, `GEOC_` vs `GC_`]
53
+ - **Top-level content folder**: [`Content/MyProject/`]
54
+ - **`Developers/` in use?**: [yes/no]
55
+
56
+ > Full prefix tables live in `/ue-naming-conventions`. Record here only what this project
57
+ > decided or does differently.
58
+
59
+ ### Code conventions
60
+
61
+ - **Naming prefixes**: [Epic standard `F`/`U`/`A`/`E`/`I`, plus any exceptions]
62
+ - **Header guards**: [`#pragma once` / traditional]
63
+ - **Log categories**: [`DEFINE_LOG_CATEGORY` names used most]
64
+ - **C++ vs Blueprint boundary**: [where the line sits, and who decides]
65
+ - **Formatting / linting**: [clang-format config, .editorconfig, enforcement]
66
+ - **Anything that deviates from Epic's conventions**: [and why]
67
+
68
+ > Record what the codebase **actually does**, not what it aspires to. These lines are what the
69
+ > `ue-*` skills follow.
70
+
71
+ ## 5. Subsystems in Use
72
+
73
+ | Subsystem | In use? | How it is used here |
74
+ |-----------|---------|---------------------|
75
+ | Gameplay Ability System | [yes/no] | [scope: all combat? just abilities?] |
76
+ | Enhanced Input | [yes/no] | [mapping context strategy] |
77
+ | CommonUI | [yes/no] | [activatable stacks, input routing] |
78
+ | Replication / multiplayer | [yes/no] | [server-authoritative? listen server? dedicated?] |
79
+ | World Partition / level streaming | [yes/no] | [data layers, streaming sources] |
80
+ | Niagara | [yes/no] | [budget expectations] |
81
+ | Mass Entity / StateTree | [yes/no] | [what runs at scale] |
82
+ | Save system | [yes/no] | [versioning approach] |
83
+
84
+ ## 6. Build Configuration
85
+
86
+ - **Target types built**: [Game, Editor, Server, Client]
87
+ - **Configurations used**: [Development, Shipping, Test, DebugGame]
88
+ - **Game Default Map**: [map path — the usual cause of a black packaged build]
89
+ - **Packaging**: [maps/directories cooked, compression, full rebuild policy]
90
+ - **CI**: [does `RunUAT BuildCookRun` run automatically? where?]
91
+ - **Platform SDKs installed**: [...]
92
+ - **Performance targets**: [target FPS, frame budget, platform floor]
93
+
94
+ ## 7. Team Context (Optional)
95
+
96
+ - **Team size and roles**: [...]
97
+ - **Source control**: [Perforce / Git + LFS / other]
98
+ - **Review expectations**: [what needs a second pair of eyes]
99
+ - **Definition of done**: [tests? functional test? packaged build verified?]
100
+ - **Known pain points**: [what keeps breaking]
101
+
102
+ ## Open Questions
103
+
104
+ - [Anything the scan could not determine and nobody has confirmed yet]
105
+
106
+ > **Rule**: an unanswered item here is an item the `ue-*` skills will have to ask about. Closing
107
+ > them is what makes the rest of the workflow fast.