unreal-engine-mcp-server 0.5.1 → 0.5.2
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/.github/workflows/publish-mcp.yml +1 -4
- package/.github/workflows/release-drafter.yml +2 -1
- package/CHANGELOG.md +38 -0
- package/dist/automation/bridge.d.ts +1 -2
- package/dist/automation/bridge.js +24 -23
- package/dist/automation/connection-manager.d.ts +1 -0
- package/dist/automation/connection-manager.js +10 -0
- package/dist/automation/message-handler.js +5 -4
- package/dist/automation/request-tracker.d.ts +4 -0
- package/dist/automation/request-tracker.js +11 -3
- package/dist/tools/actors.d.ts +19 -1
- package/dist/tools/actors.js +15 -5
- package/dist/tools/assets.js +1 -1
- package/dist/tools/blueprint.d.ts +12 -0
- package/dist/tools/blueprint.js +43 -14
- package/dist/tools/consolidated-tool-definitions.js +2 -1
- package/dist/tools/editor.js +3 -2
- package/dist/tools/handlers/actor-handlers.d.ts +1 -1
- package/dist/tools/handlers/actor-handlers.js +14 -8
- package/dist/tools/handlers/sequence-handlers.d.ts +1 -1
- package/dist/tools/handlers/sequence-handlers.js +24 -13
- package/dist/tools/introspection.d.ts +1 -1
- package/dist/tools/introspection.js +1 -1
- package/dist/tools/level.js +3 -3
- package/dist/tools/lighting.d.ts +54 -7
- package/dist/tools/lighting.js +4 -4
- package/dist/tools/materials.d.ts +1 -1
- package/dist/types/tool-types.d.ts +2 -0
- package/dist/unreal-bridge.js +4 -4
- package/dist/utils/command-validator.js +6 -5
- package/dist/utils/error-handler.d.ts +24 -2
- package/dist/utils/error-handler.js +58 -23
- package/dist/utils/normalize.d.ts +7 -4
- package/dist/utils/normalize.js +12 -10
- package/dist/utils/response-validator.js +88 -73
- package/dist/utils/unreal-command-queue.d.ts +2 -0
- package/dist/utils/unreal-command-queue.js +8 -1
- package/docs/handler-mapping.md +4 -2
- package/package.json +1 -1
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridgeSubsystem.cpp +298 -33
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_AnimationHandlers.cpp +7 -8
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintGraphHandlers.cpp +229 -319
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_BlueprintHandlers.cpp +98 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EffectHandlers.cpp +24 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_EnvironmentHandlers.cpp +96 -0
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_LightingHandlers.cpp +52 -5
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_ProcessRequest.cpp +5 -268
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpAutomationBridge_SequenceHandlers.cpp +57 -2
- package/plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/McpConnectionManager.cpp +0 -1
- package/server.json +3 -3
- package/src/automation/bridge.ts +27 -25
- package/src/automation/connection-manager.ts +18 -0
- package/src/automation/message-handler.ts +33 -8
- package/src/automation/request-tracker.ts +39 -7
- package/src/server/tool-registry.ts +3 -3
- package/src/tools/actors.ts +44 -19
- package/src/tools/assets.ts +3 -3
- package/src/tools/blueprint.ts +115 -49
- package/src/tools/consolidated-tool-definitions.ts +2 -1
- package/src/tools/editor.ts +4 -3
- package/src/tools/handlers/actor-handlers.ts +14 -9
- package/src/tools/handlers/sequence-handlers.ts +86 -63
- package/src/tools/introspection.ts +7 -7
- package/src/tools/level.ts +6 -6
- package/src/tools/lighting.ts +19 -19
- package/src/tools/materials.ts +1 -1
- package/src/tools/sequence.ts +1 -1
- package/src/tools/ui.ts +1 -1
- package/src/types/tool-types.ts +4 -0
- package/src/unreal-bridge.ts +71 -26
- package/src/utils/command-validator.ts +46 -5
- package/src/utils/error-handler.ts +128 -45
- package/src/utils/normalize.ts +38 -16
- package/src/utils/response-validator.ts +103 -87
- package/src/utils/unreal-command-queue.ts +13 -1
|
@@ -5606,6 +5606,104 @@ bool UMcpAutomationBridgeSubsystem::HandleSCSAction(
|
|
|
5606
5606
|
TEXT("Object property requires valid object path, got: %s"),
|
|
5607
5607
|
*PropertyValue);
|
|
5608
5608
|
}
|
|
5609
|
+
} else if (FStructProperty *StructProp =
|
|
5610
|
+
CastField<FStructProperty>(FoundProperty)) {
|
|
5611
|
+
// Handle struct properties (FVector, FVector2D, FLinearColor, etc.)
|
|
5612
|
+
void *PropAddr =
|
|
5613
|
+
StructProp->ContainerPtrToValuePtr<void>(ComponentTemplate);
|
|
5614
|
+
FString StructName =
|
|
5615
|
+
StructProp->Struct ? StructProp->Struct->GetName() : FString();
|
|
5616
|
+
|
|
5617
|
+
// Try to parse JSON object value from payload
|
|
5618
|
+
const TSharedPtr<FJsonObject> *JsonObjValue = nullptr;
|
|
5619
|
+
if (Payload->TryGetObjectField(TEXT("value"), JsonObjValue) &&
|
|
5620
|
+
JsonObjValue->IsValid()) {
|
|
5621
|
+
// Handle FVector explicitly
|
|
5622
|
+
if (StructName.Equals(TEXT("Vector"), ESearchCase::IgnoreCase)) {
|
|
5623
|
+
FVector *Vec = static_cast<FVector *>(PropAddr);
|
|
5624
|
+
double X = 0, Y = 0, Z = 0;
|
|
5625
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("X"), X);
|
|
5626
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Y"), Y);
|
|
5627
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Z"), Z);
|
|
5628
|
+
// Also try lowercase
|
|
5629
|
+
if (X == 0 && Y == 0 && Z == 0) {
|
|
5630
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("x"), X);
|
|
5631
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("y"), Y);
|
|
5632
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("z"), Z);
|
|
5633
|
+
}
|
|
5634
|
+
*Vec = FVector(X, Y, Z);
|
|
5635
|
+
bSuccess = true;
|
|
5636
|
+
}
|
|
5637
|
+
// Handle FVector2D
|
|
5638
|
+
else if (StructName.Equals(TEXT("Vector2D"), ESearchCase::IgnoreCase)) {
|
|
5639
|
+
FVector2D *Vec = static_cast<FVector2D *>(PropAddr);
|
|
5640
|
+
double X = 0, Y = 0;
|
|
5641
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("X"), X);
|
|
5642
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Y"), Y);
|
|
5643
|
+
if (X == 0 && Y == 0) {
|
|
5644
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("x"), X);
|
|
5645
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("y"), Y);
|
|
5646
|
+
}
|
|
5647
|
+
*Vec = FVector2D(X, Y);
|
|
5648
|
+
bSuccess = true;
|
|
5649
|
+
}
|
|
5650
|
+
// Handle FLinearColor
|
|
5651
|
+
else if (StructName.Equals(TEXT("LinearColor"),
|
|
5652
|
+
ESearchCase::IgnoreCase)) {
|
|
5653
|
+
FLinearColor *Color = static_cast<FLinearColor *>(PropAddr);
|
|
5654
|
+
double R = 0, G = 0, B = 0, A = 1;
|
|
5655
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("R"), R);
|
|
5656
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("G"), G);
|
|
5657
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("B"), B);
|
|
5658
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("A"), A);
|
|
5659
|
+
if (R == 0 && G == 0 && B == 0) {
|
|
5660
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("r"), R);
|
|
5661
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("g"), G);
|
|
5662
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("b"), B);
|
|
5663
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("a"), A);
|
|
5664
|
+
}
|
|
5665
|
+
*Color = FLinearColor(R, G, B, A);
|
|
5666
|
+
bSuccess = true;
|
|
5667
|
+
}
|
|
5668
|
+
// Handle FRotator
|
|
5669
|
+
else if (StructName.Equals(TEXT("Rotator"), ESearchCase::IgnoreCase)) {
|
|
5670
|
+
FRotator *Rot = static_cast<FRotator *>(PropAddr);
|
|
5671
|
+
double Pitch = 0, Yaw = 0, Roll = 0;
|
|
5672
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Pitch"), Pitch);
|
|
5673
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Yaw"), Yaw);
|
|
5674
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Roll"), Roll);
|
|
5675
|
+
if (Pitch == 0 && Yaw == 0 && Roll == 0) {
|
|
5676
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("pitch"), Pitch);
|
|
5677
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("yaw"), Yaw);
|
|
5678
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("roll"), Roll);
|
|
5679
|
+
}
|
|
5680
|
+
*Rot = FRotator(Pitch, Yaw, Roll);
|
|
5681
|
+
bSuccess = true;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
// Fallback: try ImportText for string representation
|
|
5686
|
+
if (!bSuccess && !PropertyValue.IsEmpty() && StructProp->Struct) {
|
|
5687
|
+
const TCHAR *Buffer = *PropertyValue;
|
|
5688
|
+
// Use UScriptStruct::ImportText (not FStructProperty)
|
|
5689
|
+
const TCHAR *Result = StructProp->Struct->ImportText(
|
|
5690
|
+
Buffer, PropAddr, nullptr, PPF_None, GWarn, StructName);
|
|
5691
|
+
bSuccess = (Result != nullptr);
|
|
5692
|
+
if (!bSuccess) {
|
|
5693
|
+
ErrorMessage = FString::Printf(
|
|
5694
|
+
TEXT("Failed to parse struct value '%s' for property '%s' of "
|
|
5695
|
+
"type '%s'. For FVector use {\"X\":val,\"Y\":val,\"Z\":val} "
|
|
5696
|
+
"or string \"(X=val,Y=val,Z=val)\""),
|
|
5697
|
+
*PropertyValue, *PropertyName, *StructName);
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
|
|
5701
|
+
if (!bSuccess && ErrorMessage.IsEmpty()) {
|
|
5702
|
+
ErrorMessage = FString::Printf(
|
|
5703
|
+
TEXT("Struct property '%s' of type '%s' requires JSON object "
|
|
5704
|
+
"value like {\"X\":val,\"Y\":val,\"Z\":val}"),
|
|
5705
|
+
*PropertyName, *StructName);
|
|
5706
|
+
}
|
|
5609
5707
|
} else {
|
|
5610
5708
|
ErrorMessage =
|
|
5611
5709
|
FString::Printf(TEXT("Property type '%s' not supported for setting"),
|
|
@@ -59,6 +59,7 @@ bool UMcpAutomationBridgeSubsystem::HandleEffectAction(
|
|
|
59
59
|
Lower.StartsWith(TEXT("create_effect"));
|
|
60
60
|
if (!bIsCreateEffect && !Lower.StartsWith(TEXT("spawn_")) &&
|
|
61
61
|
!Lower.Equals(TEXT("set_niagara_parameter")) &&
|
|
62
|
+
!Lower.Equals(TEXT("list_debug_shapes")) &&
|
|
62
63
|
!Lower.Equals(TEXT("clear_debug_shapes")))
|
|
63
64
|
return false;
|
|
64
65
|
|
|
@@ -72,6 +73,29 @@ bool UMcpAutomationBridgeSubsystem::HandleEffectAction(
|
|
|
72
73
|
ErrCode);
|
|
73
74
|
};
|
|
74
75
|
|
|
76
|
+
// Discovery: list available debug shape types
|
|
77
|
+
if (Lower.Equals(TEXT("list_debug_shapes"))) {
|
|
78
|
+
TArray<TSharedPtr<FJsonValue>> Shapes;
|
|
79
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("sphere")));
|
|
80
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("box")));
|
|
81
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("circle")));
|
|
82
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("line")));
|
|
83
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("point")));
|
|
84
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("coordinate")));
|
|
85
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("cylinder")));
|
|
86
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("cone")));
|
|
87
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("capsule")));
|
|
88
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("arrow")));
|
|
89
|
+
Shapes.Add(MakeShared<FJsonValueString>(TEXT("plane")));
|
|
90
|
+
|
|
91
|
+
TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
|
|
92
|
+
Resp->SetArrayField(TEXT("shapes"), Shapes);
|
|
93
|
+
Resp->SetNumberField(TEXT("count"), Shapes.Num());
|
|
94
|
+
SendAutomationResponse(RequestingSocket, RequestId, true,
|
|
95
|
+
TEXT("Available debug shape types"), Resp);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
// Handle create_effect tool with sub-actions
|
|
76
100
|
if (Lower.Equals(TEXT("clear_debug_shapes"))) {
|
|
77
101
|
#if WITH_EDITOR
|
|
@@ -1311,6 +1311,102 @@ bool UMcpAutomationBridgeSubsystem::HandleInspectAction(
|
|
|
1311
1311
|
TEXT("Object property requires valid object path, got: %s"),
|
|
1312
1312
|
*PropertyValue);
|
|
1313
1313
|
}
|
|
1314
|
+
} else if (FStructProperty *StructProp =
|
|
1315
|
+
CastField<FStructProperty>(FoundProperty)) {
|
|
1316
|
+
// Handle struct properties (FVector, FVector2D, FLinearColor, etc.)
|
|
1317
|
+
void *PropAddr = StructProp->ContainerPtrToValuePtr<void>(TargetObject);
|
|
1318
|
+
FString StructName =
|
|
1319
|
+
StructProp->Struct ? StructProp->Struct->GetName() : FString();
|
|
1320
|
+
|
|
1321
|
+
// Try to parse JSON object value from payload
|
|
1322
|
+
const TSharedPtr<FJsonObject> *JsonObjValue = nullptr;
|
|
1323
|
+
if (Payload->TryGetObjectField(TEXT("value"), JsonObjValue) &&
|
|
1324
|
+
JsonObjValue->IsValid()) {
|
|
1325
|
+
// Handle FVector explicitly
|
|
1326
|
+
if (StructName.Equals(TEXT("Vector"), ESearchCase::IgnoreCase)) {
|
|
1327
|
+
FVector *Vec = static_cast<FVector *>(PropAddr);
|
|
1328
|
+
double X = 0, Y = 0, Z = 0;
|
|
1329
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("X"), X);
|
|
1330
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Y"), Y);
|
|
1331
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Z"), Z);
|
|
1332
|
+
if (X == 0 && Y == 0 && Z == 0) {
|
|
1333
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("x"), X);
|
|
1334
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("y"), Y);
|
|
1335
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("z"), Z);
|
|
1336
|
+
}
|
|
1337
|
+
*Vec = FVector(X, Y, Z);
|
|
1338
|
+
bSuccess = true;
|
|
1339
|
+
}
|
|
1340
|
+
// Handle FVector2D
|
|
1341
|
+
else if (StructName.Equals(TEXT("Vector2D"), ESearchCase::IgnoreCase)) {
|
|
1342
|
+
FVector2D *Vec = static_cast<FVector2D *>(PropAddr);
|
|
1343
|
+
double X = 0, Y = 0;
|
|
1344
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("X"), X);
|
|
1345
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Y"), Y);
|
|
1346
|
+
if (X == 0 && Y == 0) {
|
|
1347
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("x"), X);
|
|
1348
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("y"), Y);
|
|
1349
|
+
}
|
|
1350
|
+
*Vec = FVector2D(X, Y);
|
|
1351
|
+
bSuccess = true;
|
|
1352
|
+
}
|
|
1353
|
+
// Handle FLinearColor
|
|
1354
|
+
else if (StructName.Equals(TEXT("LinearColor"),
|
|
1355
|
+
ESearchCase::IgnoreCase)) {
|
|
1356
|
+
FLinearColor *Color = static_cast<FLinearColor *>(PropAddr);
|
|
1357
|
+
double R = 0, G = 0, B = 0, A = 1;
|
|
1358
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("R"), R);
|
|
1359
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("G"), G);
|
|
1360
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("B"), B);
|
|
1361
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("A"), A);
|
|
1362
|
+
if (R == 0 && G == 0 && B == 0) {
|
|
1363
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("r"), R);
|
|
1364
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("g"), G);
|
|
1365
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("b"), B);
|
|
1366
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("a"), A);
|
|
1367
|
+
}
|
|
1368
|
+
*Color = FLinearColor(R, G, B, A);
|
|
1369
|
+
bSuccess = true;
|
|
1370
|
+
}
|
|
1371
|
+
// Handle FRotator
|
|
1372
|
+
else if (StructName.Equals(TEXT("Rotator"), ESearchCase::IgnoreCase)) {
|
|
1373
|
+
FRotator *Rot = static_cast<FRotator *>(PropAddr);
|
|
1374
|
+
double Pitch = 0, Yaw = 0, Roll = 0;
|
|
1375
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Pitch"), Pitch);
|
|
1376
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Yaw"), Yaw);
|
|
1377
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("Roll"), Roll);
|
|
1378
|
+
if (Pitch == 0 && Yaw == 0 && Roll == 0) {
|
|
1379
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("pitch"), Pitch);
|
|
1380
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("yaw"), Yaw);
|
|
1381
|
+
(*JsonObjValue)->TryGetNumberField(TEXT("roll"), Roll);
|
|
1382
|
+
}
|
|
1383
|
+
*Rot = FRotator(Pitch, Yaw, Roll);
|
|
1384
|
+
bSuccess = true;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// Fallback: try ImportText for string representation
|
|
1389
|
+
if (!bSuccess && !PropertyValue.IsEmpty() && StructProp->Struct) {
|
|
1390
|
+
const TCHAR *Buffer = *PropertyValue;
|
|
1391
|
+
// Use UScriptStruct::ImportText (not FStructProperty)
|
|
1392
|
+
const TCHAR *ImportResult = StructProp->Struct->ImportText(
|
|
1393
|
+
Buffer, PropAddr, nullptr, PPF_None, GWarn, StructName);
|
|
1394
|
+
bSuccess = (ImportResult != nullptr);
|
|
1395
|
+
if (!bSuccess) {
|
|
1396
|
+
ErrorMessage = FString::Printf(
|
|
1397
|
+
TEXT("Failed to parse struct value '%s' for property '%s' of "
|
|
1398
|
+
"type '%s'. For FVector use {\"X\":val,\"Y\":val,\"Z\":val} "
|
|
1399
|
+
"or string \"(X=val,Y=val,Z=val)\""),
|
|
1400
|
+
*PropertyValue, *PropertyName, *StructName);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
if (!bSuccess && ErrorMessage.IsEmpty()) {
|
|
1405
|
+
ErrorMessage = FString::Printf(
|
|
1406
|
+
TEXT("Struct property '%s' of type '%s' requires JSON object "
|
|
1407
|
+
"value like {\"X\":val,\"Y\":val,\"Z\":val}"),
|
|
1408
|
+
*PropertyName, *StructName);
|
|
1409
|
+
}
|
|
1314
1410
|
} else {
|
|
1315
1411
|
ErrorMessage =
|
|
1316
1412
|
FString::Printf(TEXT("Property type '%s' not supported for setting"),
|
|
@@ -43,6 +43,7 @@ bool UMcpAutomationBridgeSubsystem::HandleLightingAction(
|
|
|
43
43
|
!Lower.StartsWith(TEXT("setup_global_illumination")) &&
|
|
44
44
|
!Lower.StartsWith(TEXT("configure_shadows")) &&
|
|
45
45
|
!Lower.StartsWith(TEXT("set_exposure")) &&
|
|
46
|
+
!Lower.StartsWith(TEXT("list_light_types")) &&
|
|
46
47
|
!Lower.StartsWith(TEXT("set_ambient_occlusion"))) {
|
|
47
48
|
return false;
|
|
48
49
|
}
|
|
@@ -64,6 +65,38 @@ bool UMcpAutomationBridgeSubsystem::HandleLightingAction(
|
|
|
64
65
|
return true;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
if (Lower == TEXT("list_light_types")) {
|
|
69
|
+
TArray<TSharedPtr<FJsonValue>> Types;
|
|
70
|
+
// Add common shortcuts first
|
|
71
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("DirectionalLight")));
|
|
72
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("PointLight")));
|
|
73
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("SpotLight")));
|
|
74
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("RectLight")));
|
|
75
|
+
|
|
76
|
+
// Discover all ALight subclasses via reflection
|
|
77
|
+
TSet<FString> AddedNames;
|
|
78
|
+
AddedNames.Add(TEXT("DirectionalLight"));
|
|
79
|
+
AddedNames.Add(TEXT("PointLight"));
|
|
80
|
+
AddedNames.Add(TEXT("SpotLight"));
|
|
81
|
+
AddedNames.Add(TEXT("RectLight"));
|
|
82
|
+
|
|
83
|
+
for (TObjectIterator<UClass> It; It; ++It) {
|
|
84
|
+
if (It->IsChildOf(ALight::StaticClass()) &&
|
|
85
|
+
!It->HasAnyClassFlags(CLASS_Abstract) &&
|
|
86
|
+
!AddedNames.Contains(It->GetName())) {
|
|
87
|
+
Types.Add(MakeShared<FJsonValueString>(It->GetName()));
|
|
88
|
+
AddedNames.Add(It->GetName());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
|
|
93
|
+
Resp->SetArrayField(TEXT("types"), Types);
|
|
94
|
+
Resp->SetNumberField(TEXT("count"), Types.Num());
|
|
95
|
+
SendAutomationResponse(RequestingSocket, RequestId, true,
|
|
96
|
+
TEXT("Available light types"), Resp);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
if (Lower == TEXT("spawn_light")) {
|
|
68
101
|
FString LightClassStr;
|
|
69
102
|
if (!Payload->TryGetStringField(TEXT("lightClass"), LightClassStr) ||
|
|
@@ -84,11 +117,25 @@ bool UMcpAutomationBridgeSubsystem::HandleLightingAction(
|
|
|
84
117
|
else if (LightClassStr == TEXT("RectLight"))
|
|
85
118
|
LightClass = ARectLight::StaticClass();
|
|
86
119
|
else {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
120
|
+
// Dynamic fallback: Try to resolve any light class by name
|
|
121
|
+
LightClass = ResolveUClass(LightClassStr);
|
|
122
|
+
|
|
123
|
+
// Try with "A" prefix for actor classes (e.g., "SkyLight" -> "ASkyLight")
|
|
124
|
+
if (!LightClass && !LightClassStr.StartsWith(TEXT("A"))) {
|
|
125
|
+
LightClass =
|
|
126
|
+
ResolveUClass(FString::Printf(TEXT("A%s"), *LightClassStr));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Validate the resolved class is actually a light actor
|
|
130
|
+
if (!LightClass || !LightClass->IsChildOf(ALight::StaticClass())) {
|
|
131
|
+
SendAutomationError(
|
|
132
|
+
RequestingSocket, RequestId,
|
|
133
|
+
FString::Printf(
|
|
134
|
+
TEXT("Light class not found or not a light type: %s"),
|
|
135
|
+
*LightClassStr),
|
|
136
|
+
TEXT("INVALID_ARGUMENT"));
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
92
139
|
}
|
|
93
140
|
|
|
94
141
|
FVector Location = FVector::ZeroVector;
|
|
@@ -242,274 +242,11 @@ void UMcpAutomationBridgeSubsystem::ProcessAutomationRequest(
|
|
|
242
242
|
RequestingSocket);
|
|
243
243
|
}))
|
|
244
244
|
return;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return;
|
|
251
|
-
if (HandleAndLog(TEXT("HandleArrayRemove"), [&]() {
|
|
252
|
-
return HandleArrayRemove(RequestId, Action, Payload,
|
|
253
|
-
RequestingSocket);
|
|
254
|
-
}))
|
|
255
|
-
return;
|
|
256
|
-
if (HandleAndLog(TEXT("HandleArrayInsert"), [&]() {
|
|
257
|
-
return HandleArrayInsert(RequestId, Action, Payload,
|
|
258
|
-
RequestingSocket);
|
|
259
|
-
}))
|
|
260
|
-
return;
|
|
261
|
-
if (HandleAndLog(TEXT("HandleArrayGetElement"), [&]() {
|
|
262
|
-
return HandleArrayGetElement(RequestId, Action, Payload,
|
|
263
|
-
RequestingSocket);
|
|
264
|
-
}))
|
|
265
|
-
return;
|
|
266
|
-
if (HandleAndLog(TEXT("HandleArraySetElement"), [&]() {
|
|
267
|
-
return HandleArraySetElement(RequestId, Action, Payload,
|
|
268
|
-
RequestingSocket);
|
|
269
|
-
}))
|
|
270
|
-
return;
|
|
271
|
-
if (HandleAndLog(TEXT("HandleArrayClear"), [&]() {
|
|
272
|
-
return HandleArrayClear(RequestId, Action, Payload,
|
|
273
|
-
RequestingSocket);
|
|
274
|
-
}))
|
|
275
|
-
return;
|
|
276
|
-
// Map manipulation operations
|
|
277
|
-
if (HandleAndLog(TEXT("HandleMapSetValue"), [&]() {
|
|
278
|
-
return HandleMapSetValue(RequestId, Action, Payload,
|
|
279
|
-
RequestingSocket);
|
|
280
|
-
}))
|
|
281
|
-
return;
|
|
282
|
-
if (HandleAndLog(TEXT("HandleMapGetValue"), [&]() {
|
|
283
|
-
return HandleMapGetValue(RequestId, Action, Payload,
|
|
284
|
-
RequestingSocket);
|
|
285
|
-
}))
|
|
286
|
-
return;
|
|
287
|
-
if (HandleAndLog(TEXT("HandleMapRemoveKey"), [&]() {
|
|
288
|
-
return HandleMapRemoveKey(RequestId, Action, Payload,
|
|
289
|
-
RequestingSocket);
|
|
290
|
-
}))
|
|
291
|
-
return;
|
|
292
|
-
if (HandleAndLog(TEXT("HandleMapHasKey"), [&]() {
|
|
293
|
-
return HandleMapHasKey(RequestId, Action, Payload,
|
|
294
|
-
RequestingSocket);
|
|
295
|
-
}))
|
|
296
|
-
return;
|
|
297
|
-
if (HandleAndLog(TEXT("HandleMapGetKeys"), [&]() {
|
|
298
|
-
return HandleMapGetKeys(RequestId, Action, Payload,
|
|
299
|
-
RequestingSocket);
|
|
300
|
-
}))
|
|
301
|
-
return;
|
|
302
|
-
if (HandleAndLog(TEXT("HandleMapClear"), [&]() {
|
|
303
|
-
return HandleMapClear(RequestId, Action, Payload, RequestingSocket);
|
|
304
|
-
}))
|
|
305
|
-
return;
|
|
306
|
-
// Set manipulation operations
|
|
307
|
-
if (HandleAndLog(TEXT("HandleSetAdd"), [&]() {
|
|
308
|
-
return HandleSetAdd(RequestId, Action, Payload, RequestingSocket);
|
|
309
|
-
}))
|
|
310
|
-
return;
|
|
311
|
-
if (HandleAndLog(TEXT("HandleSetRemove"), [&]() {
|
|
312
|
-
return HandleSetRemove(RequestId, Action, Payload,
|
|
313
|
-
RequestingSocket);
|
|
314
|
-
}))
|
|
315
|
-
return;
|
|
316
|
-
if (HandleAndLog(TEXT("HandleSetContains"), [&]() {
|
|
317
|
-
return HandleSetContains(RequestId, Action, Payload,
|
|
318
|
-
RequestingSocket);
|
|
319
|
-
}))
|
|
320
|
-
return;
|
|
321
|
-
if (HandleAndLog(TEXT("HandleSetClear"), [&]() {
|
|
322
|
-
return HandleSetClear(RequestId, Action, Payload, RequestingSocket);
|
|
323
|
-
}))
|
|
324
|
-
return;
|
|
325
|
-
// Asset dependency graph traversal
|
|
326
|
-
if (HandleAndLog(TEXT("HandleGetAssetReferences"), [&]() {
|
|
327
|
-
return HandleGetAssetReferences(RequestId, Action, Payload,
|
|
328
|
-
RequestingSocket);
|
|
329
|
-
}))
|
|
330
|
-
return;
|
|
331
|
-
if (HandleAndLog(TEXT("HandleGetAssetDependencies"), [&]() {
|
|
332
|
-
return HandleGetAssetDependencies(RequestId, Action, Payload,
|
|
333
|
-
RequestingSocket);
|
|
334
|
-
}))
|
|
335
|
-
return;
|
|
336
|
-
// Asset workflow handlers
|
|
337
|
-
if (HandleAndLog(TEXT("HandleFixupRedirectors"), [&]() {
|
|
338
|
-
return HandleFixupRedirectors(RequestId, Action, Payload,
|
|
339
|
-
RequestingSocket);
|
|
340
|
-
}))
|
|
341
|
-
return;
|
|
342
|
-
if (HandleAndLog(TEXT("HandleSourceControlCheckout"), [&]() {
|
|
343
|
-
return HandleSourceControlCheckout(RequestId, Action, Payload,
|
|
344
|
-
RequestingSocket);
|
|
345
|
-
}))
|
|
346
|
-
return;
|
|
347
|
-
if (HandleAndLog(TEXT("HandleSourceControlSubmit"), [&]() {
|
|
348
|
-
return HandleSourceControlSubmit(RequestId, Action, Payload,
|
|
349
|
-
RequestingSocket);
|
|
350
|
-
}))
|
|
351
|
-
return;
|
|
352
|
-
if (HandleAndLog(TEXT("HandleBulkRenameAssets"), [&]() {
|
|
353
|
-
return HandleBulkRenameAssets(RequestId, Action, Payload,
|
|
354
|
-
RequestingSocket);
|
|
355
|
-
}))
|
|
356
|
-
return;
|
|
357
|
-
if (HandleAndLog(TEXT("HandleBulkDeleteAssets"), [&]() {
|
|
358
|
-
return HandleBulkDeleteAssets(RequestId, Action, Payload,
|
|
359
|
-
RequestingSocket);
|
|
360
|
-
}))
|
|
361
|
-
return;
|
|
362
|
-
if (HandleAndLog(TEXT("HandleGenerateThumbnail"), [&]() {
|
|
363
|
-
return HandleGenerateThumbnail(RequestId, Action, Payload,
|
|
364
|
-
RequestingSocket);
|
|
365
|
-
}))
|
|
366
|
-
return;
|
|
367
|
-
// Landscape operations
|
|
368
|
-
if (HandleAndLog(TEXT("HandleCreateLandscape"), [&]() {
|
|
369
|
-
return HandleCreateLandscape(RequestId, Action, Payload,
|
|
370
|
-
RequestingSocket);
|
|
371
|
-
}))
|
|
372
|
-
return;
|
|
373
|
-
if (HandleAndLog(TEXT("HandleCreateProceduralTerrain"), [&]() {
|
|
374
|
-
return HandleCreateProceduralTerrain(RequestId, Action, Payload,
|
|
375
|
-
RequestingSocket);
|
|
376
|
-
}))
|
|
377
|
-
return;
|
|
378
|
-
if (HandleAndLog(TEXT("HandleCreateLandscapeGrassType"), [&]() {
|
|
379
|
-
return HandleCreateLandscapeGrassType(RequestId, Action, Payload,
|
|
380
|
-
RequestingSocket);
|
|
381
|
-
}))
|
|
382
|
-
return;
|
|
383
|
-
if (HandleAndLog(TEXT("HandleSculptLandscape"), [&]() {
|
|
384
|
-
return HandleSculptLandscape(RequestId, Action, Payload,
|
|
385
|
-
RequestingSocket);
|
|
386
|
-
}))
|
|
387
|
-
return;
|
|
388
|
-
if (HandleAndLog(TEXT("HandleSetLandscapeMaterial"), [&]() {
|
|
389
|
-
return HandleSetLandscapeMaterial(RequestId, Action, Payload,
|
|
390
|
-
RequestingSocket);
|
|
391
|
-
}))
|
|
392
|
-
return;
|
|
393
|
-
if (HandleAndLog(TEXT("HandleEditLandscape"), [&]() {
|
|
394
|
-
return HandleEditLandscape(RequestId, Action, Payload,
|
|
395
|
-
RequestingSocket);
|
|
396
|
-
}))
|
|
397
|
-
return;
|
|
398
|
-
// Foliage operations
|
|
399
|
-
if (HandleAndLog(TEXT("HandleAddFoliageType"), [&]() {
|
|
400
|
-
return HandleAddFoliageType(RequestId, Action, Payload,
|
|
401
|
-
RequestingSocket);
|
|
402
|
-
}))
|
|
403
|
-
return;
|
|
404
|
-
if (HandleAndLog(TEXT("HandleCreateProceduralFoliage"), [&]() {
|
|
405
|
-
return HandleCreateProceduralFoliage(RequestId, Action, Payload,
|
|
406
|
-
RequestingSocket);
|
|
407
|
-
}))
|
|
408
|
-
return;
|
|
409
|
-
if (HandleAndLog(TEXT("HandlePaintFoliage"), [&]() {
|
|
410
|
-
return HandlePaintFoliage(RequestId, Action, Payload,
|
|
411
|
-
RequestingSocket);
|
|
412
|
-
}))
|
|
413
|
-
return;
|
|
414
|
-
if (HandleAndLog(TEXT("HandleAddFoliageInstances"), [&]() {
|
|
415
|
-
return HandleAddFoliageInstances(RequestId, Action, Payload,
|
|
416
|
-
RequestingSocket);
|
|
417
|
-
}))
|
|
418
|
-
return;
|
|
419
|
-
if (HandleAndLog(TEXT("HandleRemoveFoliage"), [&]() {
|
|
420
|
-
return HandleRemoveFoliage(RequestId, Action, Payload,
|
|
421
|
-
RequestingSocket);
|
|
422
|
-
}))
|
|
423
|
-
return;
|
|
424
|
-
if (HandleAndLog(TEXT("HandleGetFoliageInstances"), [&]() {
|
|
425
|
-
return HandleGetFoliageInstances(RequestId, Action, Payload,
|
|
426
|
-
RequestingSocket);
|
|
427
|
-
}))
|
|
428
|
-
return;
|
|
429
|
-
// Niagara operations
|
|
430
|
-
if (HandleAndLog(TEXT("HandleCreateNiagaraSystem"), [&]() {
|
|
431
|
-
return HandleCreateNiagaraSystem(RequestId, Action, Payload,
|
|
432
|
-
RequestingSocket);
|
|
433
|
-
}))
|
|
434
|
-
return;
|
|
435
|
-
if (HandleAndLog(TEXT("HandleCreateNiagaraRibbon"), [&]() {
|
|
436
|
-
return HandleCreateNiagaraRibbon(RequestId, Action, Payload,
|
|
437
|
-
RequestingSocket);
|
|
438
|
-
}))
|
|
439
|
-
return;
|
|
440
|
-
if (HandleAndLog(TEXT("HandleCreateNiagaraEmitter"), [&]() {
|
|
441
|
-
return HandleCreateNiagaraEmitter(RequestId, Action, Payload,
|
|
442
|
-
RequestingSocket);
|
|
443
|
-
}))
|
|
444
|
-
return;
|
|
445
|
-
if (HandleAndLog(TEXT("HandleSpawnNiagaraActor"), [&]() {
|
|
446
|
-
return HandleSpawnNiagaraActor(RequestId, Action, Payload,
|
|
447
|
-
RequestingSocket);
|
|
448
|
-
}))
|
|
449
|
-
return;
|
|
450
|
-
if (HandleAndLog(TEXT("HandleModifyNiagaraParameter"), [&]() {
|
|
451
|
-
return HandleModifyNiagaraParameter(RequestId, Action, Payload,
|
|
452
|
-
RequestingSocket);
|
|
453
|
-
}))
|
|
454
|
-
return;
|
|
455
|
-
// Animation blueprint operations
|
|
456
|
-
if (HandleAndLog(TEXT("HandleCreateAnimBlueprint"), [&]() {
|
|
457
|
-
return HandleCreateAnimBlueprint(RequestId, Action, Payload,
|
|
458
|
-
RequestingSocket);
|
|
459
|
-
}))
|
|
460
|
-
return;
|
|
461
|
-
if (HandleAndLog(TEXT("HandlePlayAnimMontage"), [&]() {
|
|
462
|
-
return HandlePlayAnimMontage(RequestId, Action, Payload,
|
|
463
|
-
RequestingSocket);
|
|
464
|
-
}))
|
|
465
|
-
return;
|
|
466
|
-
if (HandleAndLog(TEXT("HandleSetupRagdoll"), [&]() {
|
|
467
|
-
return HandleSetupRagdoll(RequestId, Action, Payload,
|
|
468
|
-
RequestingSocket);
|
|
469
|
-
}))
|
|
470
|
-
return;
|
|
471
|
-
// Material graph operations
|
|
472
|
-
if (HandleAndLog(TEXT("HandleAddMaterialTextureSample"), [&]() {
|
|
473
|
-
return HandleAddMaterialTextureSample(RequestId, Action, Payload,
|
|
474
|
-
RequestingSocket);
|
|
475
|
-
}))
|
|
476
|
-
return;
|
|
477
|
-
if (HandleAndLog(TEXT("HandleAddMaterialExpression"), [&]() {
|
|
478
|
-
return HandleAddMaterialExpression(RequestId, Action, Payload,
|
|
479
|
-
RequestingSocket);
|
|
480
|
-
}))
|
|
481
|
-
return;
|
|
482
|
-
if (HandleAndLog(TEXT("HandleCreateMaterialNodes"), [&]() {
|
|
483
|
-
return HandleCreateMaterialNodes(RequestId, Action, Payload,
|
|
484
|
-
RequestingSocket);
|
|
485
|
-
}))
|
|
486
|
-
return;
|
|
487
|
-
// Sequencer operations
|
|
488
|
-
if (HandleAndLog(TEXT("HandleAddSequencerKeyframe"), [&]() {
|
|
489
|
-
return HandleAddSequencerKeyframe(RequestId, Action, Payload,
|
|
490
|
-
RequestingSocket);
|
|
491
|
-
}))
|
|
492
|
-
return;
|
|
493
|
-
if (HandleAndLog(TEXT("HandleManageSequencerTrack"), [&]() {
|
|
494
|
-
return HandleManageSequencerTrack(RequestId, Action, Payload,
|
|
495
|
-
RequestingSocket);
|
|
496
|
-
}))
|
|
497
|
-
return;
|
|
498
|
-
if (HandleAndLog(TEXT("HandleAddCameraTrack"), [&]() {
|
|
499
|
-
return HandleAddCameraTrack(RequestId, Action, Payload,
|
|
500
|
-
RequestingSocket);
|
|
501
|
-
}))
|
|
502
|
-
return;
|
|
503
|
-
if (HandleAndLog(TEXT("HandleAddAnimationTrack"), [&]() {
|
|
504
|
-
return HandleAddAnimationTrack(RequestId, Action, Payload,
|
|
505
|
-
RequestingSocket);
|
|
506
|
-
}))
|
|
507
|
-
return;
|
|
508
|
-
if (HandleAndLog(TEXT("HandleAddTransformTrack"), [&]() {
|
|
509
|
-
return HandleAddTransformTrack(RequestId, Action, Payload,
|
|
510
|
-
RequestingSocket);
|
|
511
|
-
}))
|
|
512
|
-
return;
|
|
245
|
+
|
|
246
|
+
// Specialized actions (Array, Map, Set, Landscape, Foliage, Niagara,
|
|
247
|
+
// Animation, Sequencer, etc.) are now handled by the O(1)
|
|
248
|
+
// AutomationHandlers registry check at the top of this function. The
|
|
249
|
+
// linear checks have been removed for performance.
|
|
513
250
|
|
|
514
251
|
// Delegate asset/control/blueprint/sequence actions to their handlers
|
|
515
252
|
UE_LOG(LogMcpAutomationBridgeSubsystem, Verbose,
|
|
@@ -2375,6 +2375,39 @@ bool UMcpAutomationBridgeSubsystem::HandleSequenceAction(
|
|
|
2375
2375
|
if (EffectiveAction == TEXT("sequence_remove_track"))
|
|
2376
2376
|
return HandleSequenceRemoveTrack(RequestId, LocalPayload, RequestingSocket);
|
|
2377
2377
|
|
|
2378
|
+
if (EffectiveAction == TEXT("sequence_list_track_types")) {
|
|
2379
|
+
// Discovery: list available track types
|
|
2380
|
+
TArray<TSharedPtr<FJsonValue>> Types;
|
|
2381
|
+
// Add common shortcuts first
|
|
2382
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("transform")));
|
|
2383
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("3dtransform")));
|
|
2384
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("audio")));
|
|
2385
|
+
Types.Add(MakeShared<FJsonValueString>(TEXT("event")));
|
|
2386
|
+
|
|
2387
|
+
// Discover all UMovieSceneTrack subclasses via reflection
|
|
2388
|
+
TSet<FString> AddedNames;
|
|
2389
|
+
AddedNames.Add(TEXT("transform"));
|
|
2390
|
+
AddedNames.Add(TEXT("3dtransform"));
|
|
2391
|
+
AddedNames.Add(TEXT("audio"));
|
|
2392
|
+
AddedNames.Add(TEXT("event"));
|
|
2393
|
+
|
|
2394
|
+
for (TObjectIterator<UClass> It; It; ++It) {
|
|
2395
|
+
if (It->IsChildOf(UMovieSceneTrack::StaticClass()) &&
|
|
2396
|
+
!It->HasAnyClassFlags(CLASS_Abstract) &&
|
|
2397
|
+
!AddedNames.Contains(It->GetName())) {
|
|
2398
|
+
Types.Add(MakeShared<FJsonValueString>(It->GetName()));
|
|
2399
|
+
AddedNames.Add(It->GetName());
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
TSharedPtr<FJsonObject> Resp = MakeShared<FJsonObject>();
|
|
2404
|
+
Resp->SetArrayField(TEXT("types"), Types);
|
|
2405
|
+
Resp->SetNumberField(TEXT("count"), Types.Num());
|
|
2406
|
+
SendAutomationResponse(RequestingSocket, RequestId, true,
|
|
2407
|
+
TEXT("Available track types"), Resp);
|
|
2408
|
+
return true;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2378
2411
|
if (EffectiveAction == TEXT("sequence_add_track")) {
|
|
2379
2412
|
// add_track action: Add a track to a binding in a level sequence
|
|
2380
2413
|
FString SeqPath = ResolveSequencePath(LocalPayload);
|
|
@@ -2481,8 +2514,30 @@ bool UMcpAutomationBridgeSubsystem::HandleSequenceAction(
|
|
|
2481
2514
|
}
|
|
2482
2515
|
}
|
|
2483
2516
|
|
|
2484
|
-
//
|
|
2485
|
-
|
|
2517
|
+
// Dynamic fallback: Try to resolve any track class by name
|
|
2518
|
+
if (!NewTrack) {
|
|
2519
|
+
UClass *TrackClass = ResolveUClass(TrackType);
|
|
2520
|
+
|
|
2521
|
+
// Try with common prefixes
|
|
2522
|
+
if (!TrackClass) {
|
|
2523
|
+
TrackClass = ResolveUClass(
|
|
2524
|
+
FString::Printf(TEXT("UMovieScene%sTrack"), *TrackType));
|
|
2525
|
+
}
|
|
2526
|
+
if (!TrackClass) {
|
|
2527
|
+
TrackClass = ResolveUClass(
|
|
2528
|
+
FString::Printf(TEXT("MovieScene%sTrack"), *TrackType));
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// Validate it's actually a track class
|
|
2532
|
+
if (TrackClass &&
|
|
2533
|
+
TrackClass->IsChildOf(UMovieSceneTrack::StaticClass())) {
|
|
2534
|
+
if (BindingGuid.IsValid()) {
|
|
2535
|
+
NewTrack = MovieScene->AddTrack(TrackClass, BindingGuid);
|
|
2536
|
+
} else {
|
|
2537
|
+
NewTrack = MovieScene->AddTrack(TrackClass);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2486
2541
|
|
|
2487
2542
|
if (NewTrack) {
|
|
2488
2543
|
Sequence->MarkPackageDirty();
|