claude-code-arcane 2.3.0 → 2.5.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.
- package/CHANGELOG.md +23 -0
- package/README.md +131 -41
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +642 -478
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +28 -54
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +2 -1
- package/profiles/agile.yaml +1 -1
- package/profiles/ai.yaml +1 -1
- package/profiles/android-native.yaml +1 -1
- package/profiles/audio.yaml +1 -1
- package/profiles/backend-dotnet.yaml +1 -1
- package/profiles/backend-go.yaml +1 -1
- package/profiles/backend-nestjs.yaml +1 -1
- package/profiles/backend-nextjs.yaml +1 -1
- package/profiles/backend-ts.yaml +1 -1
- package/profiles/business.yaml +1 -1
- package/profiles/clevel.yaml +1 -1
- package/profiles/clickup.yaml +1 -1
- package/profiles/core.yaml +1 -1
- package/profiles/database.yaml +1 -1
- package/profiles/design.yaml +1 -1
- package/profiles/docs.yaml +1 -1
- package/profiles/ecommerce.yaml +1 -1
- package/profiles/finance.yaml +1 -1
- package/profiles/flutter.yaml +1 -1
- package/profiles/freelance.yaml +1 -1
- package/profiles/frontend.yaml +1 -1
- package/profiles/infra.yaml +1 -1
- package/profiles/integrations.yaml +1 -1
- package/profiles/ios-native.yaml +1 -1
- package/profiles/jira.yaml +1 -1
- package/profiles/job-hunt.yaml +1 -1
- package/profiles/marketing.yaml +1 -1
- package/profiles/mobile.yaml +1 -1
- package/profiles/regulatory.yaml +1 -1
- package/profiles/second-brain.yaml +1 -1
- package/profiles/security.yaml +1 -1
- package/profiles/self-improving.yaml +1 -1
- package/profiles/statusline.yaml +1 -1
- package/profiles/testing.yaml +1 -1
- package/profiles/unity-design.yaml +1 -1
- package/profiles/unity-dev.yaml +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/profiles/visual-novel.yaml +1 -1
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/arcane-list/SKILL.md +12 -7
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Game Feature Patterns Reference
|
|
2
|
+
|
|
3
|
+
Complete code templates for Game Feature plugin setup, custom actions, and component injection.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## GameFeature .uplugin Template
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"FileVersion": 3,
|
|
12
|
+
"Version": 1,
|
|
13
|
+
"VersionName": "1.0",
|
|
14
|
+
"FriendlyName": "My Feature",
|
|
15
|
+
"Description": "Adds my gameplay feature",
|
|
16
|
+
"Category": "Game Features",
|
|
17
|
+
"CreatedBy": "",
|
|
18
|
+
"CreatedByURL": "",
|
|
19
|
+
"DocsURL": "",
|
|
20
|
+
"MarketplaceURL": "",
|
|
21
|
+
"CanContainContent": true,
|
|
22
|
+
"Installed": false,
|
|
23
|
+
"ExplicitlyLoaded": true,
|
|
24
|
+
"Type": "GameFeature",
|
|
25
|
+
"BuiltInInitialFeatureState": "Active",
|
|
26
|
+
"Plugins": [
|
|
27
|
+
{ "Name": "GameFeatures", "Enabled": true },
|
|
28
|
+
{ "Name": "ModularGameplay", "Enabled": true }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`BuiltInInitialFeatureState` options: `"Installed"` (on disk, not registered), `"Registered"` (registered with Asset Manager, activated by code), `"Active"` (fully active on startup).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Custom UGameFeatureAction Subclass
|
|
38
|
+
|
|
39
|
+
```cpp
|
|
40
|
+
// MyGameFeatureAction_SpawnManagers.h
|
|
41
|
+
#pragma once
|
|
42
|
+
#include "GameFeatureAction.h"
|
|
43
|
+
#include "GameFeaturesSubsystem.h"
|
|
44
|
+
#include "MyGameFeatureAction_SpawnManagers.generated.h"
|
|
45
|
+
|
|
46
|
+
UCLASS(MinimalAPI, meta = (DisplayName = "Spawn Managers"))
|
|
47
|
+
class UMyGameFeatureAction_SpawnManagers : public UGameFeatureAction
|
|
48
|
+
{
|
|
49
|
+
GENERATED_BODY()
|
|
50
|
+
|
|
51
|
+
public:
|
|
52
|
+
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override;
|
|
53
|
+
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override;
|
|
54
|
+
|
|
55
|
+
private:
|
|
56
|
+
void AddToWorld(const FWorldContext& WorldContext);
|
|
57
|
+
void HandleGameInstanceStart(UGameInstance* GameInstance);
|
|
58
|
+
|
|
59
|
+
// Per-world tracking for PIE support
|
|
60
|
+
TMap<FObjectKey, TArray<TWeakObjectPtr<AActor>>> SpawnedActorsPerWorld;
|
|
61
|
+
|
|
62
|
+
UPROPERTY(EditAnywhere, Category = "Spawn")
|
|
63
|
+
TSubclassOf<AActor> ManagerClass;
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```cpp
|
|
68
|
+
// MyGameFeatureAction_SpawnManagers.cpp
|
|
69
|
+
#include "MyGameFeatureAction_SpawnManagers.h"
|
|
70
|
+
#include "Engine/GameInstance.h"
|
|
71
|
+
#include "Engine/World.h"
|
|
72
|
+
|
|
73
|
+
void UMyGameFeatureAction_SpawnManagers::OnGameFeatureActivating(
|
|
74
|
+
FGameFeatureActivatingContext& Context)
|
|
75
|
+
{
|
|
76
|
+
// Handle worlds already running (hot-activation)
|
|
77
|
+
for (const FWorldContext& WorldContext : GEngine->GetWorldContexts())
|
|
78
|
+
{
|
|
79
|
+
if (Context.ShouldApplyToWorldContext(WorldContext))
|
|
80
|
+
{
|
|
81
|
+
AddToWorld(WorldContext);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Handle worlds created after activation (new PIE sessions, travel)
|
|
85
|
+
FWorldDelegates::OnStartGameInstance.AddUObject(
|
|
86
|
+
this, &UMyGameFeatureAction_SpawnManagers::HandleGameInstanceStart);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void UMyGameFeatureAction_SpawnManagers::OnGameFeatureDeactivating(
|
|
90
|
+
FGameFeatureDeactivatingContext& Context)
|
|
91
|
+
{
|
|
92
|
+
FWorldDelegates::OnStartGameInstance.RemoveAll(this);
|
|
93
|
+
|
|
94
|
+
for (auto& [WorldKey, ActorArray] : SpawnedActorsPerWorld)
|
|
95
|
+
{
|
|
96
|
+
for (TWeakObjectPtr<AActor>& ActorPtr : ActorArray)
|
|
97
|
+
{
|
|
98
|
+
if (AActor* Actor = ActorPtr.Get())
|
|
99
|
+
{
|
|
100
|
+
Actor->Destroy();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
SpawnedActorsPerWorld.Empty();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void UMyGameFeatureAction_SpawnManagers::AddToWorld(const FWorldContext& WorldContext)
|
|
108
|
+
{
|
|
109
|
+
UWorld* World = WorldContext.World();
|
|
110
|
+
if (!World || !World->IsGameWorld() || !ManagerClass)
|
|
111
|
+
{
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
FActorSpawnParameters SpawnParams;
|
|
116
|
+
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
117
|
+
AActor* Spawned = World->SpawnActor<AActor>(ManagerClass, FTransform::Identity, SpawnParams);
|
|
118
|
+
if (Spawned)
|
|
119
|
+
{
|
|
120
|
+
SpawnedActorsPerWorld.FindOrAdd(FObjectKey(World)).Add(Spawned);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void UMyGameFeatureAction_SpawnManagers::HandleGameInstanceStart(UGameInstance* GameInstance)
|
|
125
|
+
{
|
|
126
|
+
if (UWorld* World = GameInstance->GetWorld())
|
|
127
|
+
{
|
|
128
|
+
for (const FWorldContext& WC : GEngine->GetWorldContexts())
|
|
129
|
+
{
|
|
130
|
+
if (WC.World() == World) { AddToWorld(WC); break; }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Key pattern:** Iterate `GEngine->GetWorldContexts()` and check `Context.ShouldApplyToWorldContext()` to support PIE with multiple worlds. Store per-world state with `FObjectKey` as map key.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Actor Receiver Setup
|
|
141
|
+
|
|
142
|
+
```cpp
|
|
143
|
+
// MyModularCharacter.h — registers for component injection
|
|
144
|
+
#include "GameFrameworkComponentManager.h"
|
|
145
|
+
|
|
146
|
+
void AMyModularCharacter::BeginPlay()
|
|
147
|
+
{
|
|
148
|
+
Super::BeginPlay();
|
|
149
|
+
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void AMyModularCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
153
|
+
{
|
|
154
|
+
UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
|
|
155
|
+
Super::EndPlay(EndPlayReason);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Call `RemoveGameFrameworkComponentReceiver` before `Super::EndPlay` to ensure cleanup occurs while the actor is still valid.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Extension Handler Registration
|
|
164
|
+
|
|
165
|
+
```cpp
|
|
166
|
+
// In a custom UGameFeatureAction or subsystem
|
|
167
|
+
void UMyAction::OnGameFeatureActivating(FGameFeatureActivatingContext& Context)
|
|
168
|
+
{
|
|
169
|
+
UGameFrameworkComponentManager* CompMgr =
|
|
170
|
+
UGameInstance::GetSubsystem<UGameFrameworkComponentManager>(
|
|
171
|
+
/* get game instance from world context */);
|
|
172
|
+
|
|
173
|
+
ExtensionHandle = CompMgr->AddExtensionHandler(
|
|
174
|
+
TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
|
|
175
|
+
FExtensionHandlerDelegate::CreateUObject(
|
|
176
|
+
this, &UMyAction::OnActorExtension));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void UMyAction::OnActorExtension(AActor* Actor, FName EventName)
|
|
180
|
+
{
|
|
181
|
+
if (EventName == UGameFrameworkComponentManager::NAME_ReceiverAdded)
|
|
182
|
+
{
|
|
183
|
+
// Actor just registered — configure initial state
|
|
184
|
+
}
|
|
185
|
+
else if (EventName == UGameFrameworkComponentManager::NAME_GameActorReady)
|
|
186
|
+
{
|
|
187
|
+
// All init states complete — safe to use all components
|
|
188
|
+
}
|
|
189
|
+
else if (EventName == UGameFrameworkComponentManager::NAME_ReceiverRemoved)
|
|
190
|
+
{
|
|
191
|
+
// Actor unregistering — clean up references
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
void UMyAction::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
|
|
196
|
+
{
|
|
197
|
+
// RAII: dropping the handle removes the extension
|
|
198
|
+
ExtensionHandle.Reset();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Member variable — RAII handle
|
|
202
|
+
TSharedPtr<FComponentRequestHandle> ExtensionHandle;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## FComponentRequestHandle RAII Lifecycle
|
|
208
|
+
|
|
209
|
+
```cpp
|
|
210
|
+
// Store handles as member variables for lifetime management
|
|
211
|
+
// Note: TSharedPtr is not supported by UE reflection — no UPROPERTY() here
|
|
212
|
+
TArray<TSharedPtr<FComponentRequestHandle>> ComponentRequests;
|
|
213
|
+
|
|
214
|
+
void UMyAction::OnGameFeatureActivating(FGameFeatureActivatingContext& Context)
|
|
215
|
+
{
|
|
216
|
+
UGameFrameworkComponentManager* CompMgr = /* ... */;
|
|
217
|
+
|
|
218
|
+
// Each request returns a shared handle — RAII removes on destruction
|
|
219
|
+
ComponentRequests.Add(CompMgr->AddComponentRequest(
|
|
220
|
+
TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
|
|
221
|
+
UMyHealthComponent::StaticClass(),
|
|
222
|
+
EGameFrameworkAddComponentFlags::AddUnique));
|
|
223
|
+
|
|
224
|
+
ComponentRequests.Add(CompMgr->AddComponentRequest(
|
|
225
|
+
TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
|
|
226
|
+
UMyInventoryComponent::StaticClass(),
|
|
227
|
+
EGameFrameworkAddComponentFlags::AddUnique));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
void UMyAction::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
|
|
231
|
+
{
|
|
232
|
+
// Empty the array — shared pointers destruct — components removed automatically
|
|
233
|
+
ComponentRequests.Empty();
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Custom Action with Async Deactivation
|
|
240
|
+
|
|
241
|
+
```cpp
|
|
242
|
+
UCLASS(MinimalAPI, meta = (DisplayName = "Save Progress On Deactivate"))
|
|
243
|
+
class UMyAction_SaveProgress : public UGameFeatureAction
|
|
244
|
+
{
|
|
245
|
+
GENERATED_BODY()
|
|
246
|
+
public:
|
|
247
|
+
virtual void OnGameFeatureDeactivating(
|
|
248
|
+
FGameFeatureDeactivatingContext& Context) override
|
|
249
|
+
{
|
|
250
|
+
// Pause deactivation — returns FSimpleDelegate that MUST be invoked
|
|
251
|
+
FSimpleDelegate Resume = Context.PauseDeactivationUntilComplete(
|
|
252
|
+
TEXT("SaveProgress"));
|
|
253
|
+
|
|
254
|
+
// Start async save
|
|
255
|
+
UGameplayStatics::AsyncSaveGameToSlot(
|
|
256
|
+
SaveGameObject, TEXT("AutoSave"), 0,
|
|
257
|
+
FAsyncSaveGameToSlotDelegate::CreateLambda(
|
|
258
|
+
[Resume](const FString&, const int32, bool bSuccess)
|
|
259
|
+
{
|
|
260
|
+
UE_LOG(LogTemp, Log, TEXT("Save %s"),
|
|
261
|
+
bSuccess ? TEXT("succeeded") : TEXT("failed"));
|
|
262
|
+
Resume.ExecuteIfBound(); // Resume deactivation
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private:
|
|
267
|
+
UPROPERTY()
|
|
268
|
+
TObjectPtr<USaveGame> SaveGameObject;
|
|
269
|
+
};
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Critical:** Always invoke the resume delegate, even on error paths. If the async operation can fail silently (timeout, crash), wrap with a safety timer.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Project Policies Subclass
|
|
277
|
+
|
|
278
|
+
```cpp
|
|
279
|
+
UCLASS()
|
|
280
|
+
class UMyProjectPolicies : public UGameFeaturesProjectPolicies
|
|
281
|
+
{
|
|
282
|
+
GENERATED_BODY()
|
|
283
|
+
public:
|
|
284
|
+
virtual bool IsPluginAllowed(const FString& PluginURL, FString* OutReason) const override
|
|
285
|
+
{
|
|
286
|
+
// Block specific features in shipping builds
|
|
287
|
+
if (PluginURL.Contains(TEXT("DebugTools")))
|
|
288
|
+
{
|
|
289
|
+
if (OutReason) { *OutReason = TEXT("Debug tools disabled in shipping"); }
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
virtual void GetGameFeatureLoadingMode(
|
|
296
|
+
bool& bLoadClientData, bool& bLoadServerData) const override
|
|
297
|
+
{
|
|
298
|
+
bLoadClientData = !IsRunningDedicatedServer();
|
|
299
|
+
bLoadServerData = true;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Register in `DefaultGame.ini`:
|
|
305
|
+
```ini
|
|
306
|
+
[/Script/GameFeatures.GameFeaturesSubsystemSettings]
|
|
307
|
+
GameFeaturesManagerClassName=/Script/MyProject.UMyProjectPolicies
|
|
308
|
+
```
|