ue-mcp 1.4.11-beta.7 → 1.4.11-beta.8
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/dist/tool-counts.json +2 -2
- package/dist/tools/pie.js +3 -3
- package/dist/tools/pie.js.map +1 -1
- package/dist/ue-mcp.default.yml +18 -14
- package/package.json +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEHandlers_Script.cpp +33 -20
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEScriptValidation.cpp +22 -11
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEScriptValidation.h +15 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PIEScriptSaveWarningsTests.cpp +163 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PIEScriptValidationTests.cpp +7 -0
- package/plugin/ue_mcp_bridge/UE_MCP_Bridge.uplugin +1 -1
package/dist/tool-counts.json
CHANGED
package/dist/tools/pie.js
CHANGED
|
@@ -51,10 +51,10 @@ export const pieTool = categoryTool("pie", "PIE testing: record, replay and obse
|
|
|
51
51
|
session_errors: specBp("read", "Deduped errors and warnings logged during a PIE session, with Blueprint exceptions.", "pie_session_errors"),
|
|
52
52
|
session_log: specBp("read", "Paged Output Log lines captured during a PIE session, with filters.", "pie_session_log"),
|
|
53
53
|
perf_summary: specBp("read", "A recording's frametime avg/p50/p99/max, game/render/gpu ms, peak memory and worst hitches.", "pie_perf_summary"),
|
|
54
|
-
script_create: specBp("mutate", "Create a test script asset: steps of {instruction, judge auto|manual, arrange, act, expect {value, op decreasing|increasing|eq|gt|lt|visible|exists, target, within_ms}}. arrange takes a scenario's spawn|set|call entries and act takes uat_run's step actions (place, press, inject, hold, tape, console, capture, wait_ms); they run in that order before the expect window, which counts from the end of act.
|
|
54
|
+
script_create: specBp("mutate", "Create a test script asset: steps of {instruction, judge auto|manual, arrange, act, expect {value, op decreasing|increasing|eq|gt|lt|visible|exists, target, within_ms}}. arrange takes a scenario's spawn|set|call entries and act takes uat_run's step actions (place, press, inject, hold, tape, console, capture, wait_ms); they run in that order before the expect window, which counts from the end of act. Always saves, returning what the check finds as warnings [{step, field, message, candidates}]: an expect.value the level, player pawn and profile do not resolve, an op or target the value's type cannot meet, an actor, actor class or Input Action arrange and act name that does not exist, or no auto step without manual_only=true. resolve_in_pie=true on a step defers a value that only exists in PIE. Refused (INVALID_SCRIPT_STEPS, errors in the same shape) only when the script cannot be written: no steps, a step that does not parse, or a map (e.g. /Game/Maps/L_Arena) that is not a level.", "pie_script_create"),
|
|
55
55
|
script_read: specBp("read", "Read a test script.", "pie_script_read"),
|
|
56
|
-
script_update: specBp("mutate", "Update a test script; steps given replace the stored ones. Checks the result like script_create when steps, profile, map or manual_only change.", "pie_script_update"),
|
|
57
|
-
script_validate: specBp("read", "Check steps the way script_create does without saving anything: valid, and errors [{step, field, message, candidates}] for every
|
|
56
|
+
script_update: specBp("mutate", "Update a test script; steps given replace the stored ones. Checks the result like script_create when steps, profile, map or manual_only change, and saves with its findings as warnings; refuses only what cannot be written.", "pie_script_update"),
|
|
57
|
+
script_validate: specBp("read", "Check steps the way script_create does without saving anything: valid, and errors [{step, field, message, candidates}] for every finding script_create would return as a warning or refuse.", "pie_script_validate"),
|
|
58
58
|
script_values: specBp("read", "What a script can name in the open level: the player pawn's readable value paths with kind and type, level actors, classes the game mode spawns, Widget Blueprints and the profile's tracked values. target adds which actors are of that class and whether it is the player pawn.", "pie_script_values"),
|
|
59
59
|
script_delete: { ...specBp("mutate", "Delete a test script (confirm=true).", "pie_script_delete"), destructive: true },
|
|
60
60
|
script_list: specBp("read", "List test scripts, each with map and last_run: its newest run's run_id, state, started_at, ended_at, summary and results_path, or null.", "pie_script_list"),
|
package/dist/tools/pie.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pie.js","sourceRoot":"","sources":["../../src/tools/pie.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"pie.js","sourceRoot":"","sources":["../../src/tools/pie.ts"],"names":[],"mappings":"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"}
|
package/dist/ue-mcp.default.yml
CHANGED
|
@@ -15464,15 +15464,17 @@ tasks:
|
|
|
15464
15464
|
arrange takes a scenario's spawn|set|call entries and act takes uat_run's
|
|
15465
15465
|
step actions (place, press, inject, hold, tape, console, capture,
|
|
15466
15466
|
wait_ms); they run in that order before the expect window, which counts
|
|
15467
|
-
from the end of act.
|
|
15468
|
-
field, message, candidates}]: an expect.value the level,
|
|
15469
|
-
profile do not resolve, an op or target the value's type
|
|
15470
|
-
an actor, actor class or Input Action arrange and act name
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
|
|
15467
|
+
from the end of act. Always saves, returning what the check finds as
|
|
15468
|
+
warnings [{step, field, message, candidates}]: an expect.value the level,
|
|
15469
|
+
player pawn and profile do not resolve, an op or target the value's type
|
|
15470
|
+
cannot meet, an actor, actor class or Input Action arrange and act name
|
|
15471
|
+
that does not exist, or no auto step without manual_only=true.
|
|
15472
|
+
resolve_in_pie=true on a step defers a value that only exists in PIE.
|
|
15473
|
+
Refused (INVALID_SCRIPT_STEPS, errors in the same shape) only when the
|
|
15474
|
+
script cannot be written: no steps, a step that does not parse, or a map
|
|
15475
|
+
(e.g. /Game/Maps/L_Arena) that is not a level. Params: name,
|
|
15476
|
+
package_path?, steps, description?, profile?, map?, default_within_ms?,
|
|
15477
|
+
manual_only?, onConflict?
|
|
15476
15478
|
pie.script_read:
|
|
15477
15479
|
class_path: pie.script_read
|
|
15478
15480
|
group: pie
|
|
@@ -15482,16 +15484,18 @@ tasks:
|
|
|
15482
15484
|
group: pie
|
|
15483
15485
|
description: >-
|
|
15484
15486
|
Update a test script; steps given replace the stored ones. Checks the
|
|
15485
|
-
result like script_create when steps, profile, map or manual_only change
|
|
15486
|
-
|
|
15487
|
-
|
|
15487
|
+
result like script_create when steps, profile, map or manual_only change,
|
|
15488
|
+
and saves with its findings as warnings; refuses only what cannot be
|
|
15489
|
+
written. Params: path, steps?, description?, profile?, map?,
|
|
15490
|
+
default_within_ms?, manual_only?
|
|
15488
15491
|
pie.script_validate:
|
|
15489
15492
|
class_path: pie.script_validate
|
|
15490
15493
|
group: pie
|
|
15491
15494
|
description: >-
|
|
15492
15495
|
Check steps the way script_create does without saving anything: valid, and
|
|
15493
|
-
errors [{step, field, message, candidates}] for every
|
|
15494
|
-
|
|
15496
|
+
errors [{step, field, message, candidates}] for every finding
|
|
15497
|
+
script_create would return as a warning or refuse. Params: steps,
|
|
15498
|
+
profile?, map?, manual_only?
|
|
15495
15499
|
pie.script_values:
|
|
15496
15500
|
class_path: pie.script_values
|
|
15497
15501
|
group: pie
|
package/package.json
CHANGED
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEHandlers_Script.cpp
CHANGED
|
@@ -78,10 +78,10 @@ namespace
|
|
|
78
78
|
return Profile.IsEmpty() || LoadObject<UMCPPIEObservationProfile>(nullptr, *Profile) != nullptr;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// The checks script_create and
|
|
82
|
-
// one the observer can check against the level and that profile. Issues already
|
|
83
|
-
// failures) come in through Issues
|
|
84
|
-
|
|
81
|
+
// The checks script_create, script_update and script_validate share: the profile and map exist,
|
|
82
|
+
// and each step is one the observer can check against the level and that profile. Issues already
|
|
83
|
+
// found (parse failures) come in through Issues, and every finding is added to it.
|
|
84
|
+
void CheckTestScript(const TArray<FScriptStepDef>& Steps, const FString& Profile, const FString& Map,
|
|
85
85
|
bool bManualOnly, TArray<FScriptStepIssue>& Issues)
|
|
86
86
|
{
|
|
87
87
|
TSet<int32> Failed;
|
|
@@ -113,6 +113,7 @@ namespace
|
|
|
113
113
|
FScriptStepIssue Issue;
|
|
114
114
|
Issue.Field = TEXT("map");
|
|
115
115
|
Issue.Message = FString::Printf(TEXT("Map not found: %s is not a level asset"), *MCPMapPackageName(Map));
|
|
116
|
+
Issue.bBlocksSave = true;
|
|
116
117
|
Issue.Candidates = RankScriptCandidates(MCPMapPackageName(Map), Maps);
|
|
117
118
|
Issues.Add(MoveTemp(Issue));
|
|
118
119
|
}
|
|
@@ -127,8 +128,18 @@ namespace
|
|
|
127
128
|
}
|
|
128
129
|
ValidateScriptSteps(Steps, bManualOnly, Ctx, Issues, Failed);
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// script_create and script_update refuse only what cannot be written; every other finding is
|
|
134
|
+
// returned with the save as warnings, since a value may only resolve once PIE runs.
|
|
135
|
+
TSharedPtr<FJsonValue> RefuseUnsavableScript(const TArray<FScriptStepIssue>& Issues, TArray<FScriptStepIssue>& OutWarnings)
|
|
136
|
+
{
|
|
137
|
+
TArray<FScriptStepIssue> Blocking;
|
|
138
|
+
SplitScriptIssues(Issues, Blocking, OutWarnings);
|
|
139
|
+
if (Blocking.Num() == 0) return nullptr;
|
|
140
|
+
TSharedPtr<FJsonObject> Obj = ScriptIssuesErrorObject(Blocking, TEXT("The script cannot be saved as written"));
|
|
141
|
+
Obj->SetArrayField(TEXT("warnings"), ScriptIssuesToJson(OutWarnings));
|
|
142
|
+
return MakeShared<FJsonValueObject>(Obj);
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
FString ScriptVerdictForStatus(EScriptStepStatus Status)
|
|
@@ -402,7 +413,9 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptCreate(const TSharedPtr<FJsonObjec
|
|
|
402
413
|
TArray<FScriptStepDef> Steps;
|
|
403
414
|
TArray<FScriptStepIssue> Issues;
|
|
404
415
|
ParseScriptStepsJsonIssues(*StepsArr, Steps, Issues);
|
|
405
|
-
|
|
416
|
+
CheckTestScript(Steps, Profile, Map, bManualOnly, Issues);
|
|
417
|
+
TArray<FScriptStepIssue> Warnings;
|
|
418
|
+
if (TSharedPtr<FJsonValue> Refusal = RefuseUnsavableScript(Issues, Warnings)) return Refusal;
|
|
406
419
|
|
|
407
420
|
const FString AssetPath = PackagePath / Name;
|
|
408
421
|
if (OnConflict == TEXT("update") || OnConflict == TEXT("replace"))
|
|
@@ -430,6 +443,7 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptCreate(const TSharedPtr<FJsonObjec
|
|
|
430
443
|
MCPSetRollback(Result, TEXT("pie_script_update"), Inverse);
|
|
431
444
|
}
|
|
432
445
|
TestScriptFieldsToJson(Existing, AssetPath, Result);
|
|
446
|
+
Result->SetArrayField(TEXT("warnings"), ScriptIssuesToJson(Warnings));
|
|
433
447
|
return MCPResult(Result);
|
|
434
448
|
}
|
|
435
449
|
}
|
|
@@ -454,6 +468,7 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptCreate(const TSharedPtr<FJsonObjec
|
|
|
454
468
|
auto Result = MCPSuccess();
|
|
455
469
|
MCPSetCreated(Result);
|
|
456
470
|
TestScriptFieldsToJson(Script, AssetPath, Result);
|
|
471
|
+
Result->SetArrayField(TEXT("warnings"), ScriptIssuesToJson(Warnings));
|
|
457
472
|
TSharedPtr<FJsonObject> Payload = MakeShared<FJsonObject>();
|
|
458
473
|
Payload->SetStringField(TEXT("path"), AssetPath);
|
|
459
474
|
Payload->SetBoolField(TEXT("confirm"), true);
|
|
@@ -499,19 +514,18 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptUpdate(const TSharedPtr<FJsonObjec
|
|
|
499
514
|
UMCPPIETestScript* Script = LoadTestScriptAsset(Path);
|
|
500
515
|
if (!Script) return MCPError(FString::Printf(TEXT("Test script not found: %s"), *Path));
|
|
501
516
|
|
|
502
|
-
// The script as it
|
|
517
|
+
// The script as it will stand after the update is checked whenever its steps, profile, map or
|
|
503
518
|
// manual_only change; a description or window change alone leaves the steps as they were.
|
|
504
519
|
TArray<FScriptStepDef> Steps;
|
|
520
|
+
TArray<FScriptStepIssue> Warnings;
|
|
505
521
|
if (bHasSteps || bHasProfile || bHasMap || bHasManualOnly)
|
|
506
522
|
{
|
|
507
523
|
TArray<FScriptStepIssue> Issues;
|
|
508
524
|
if (bHasSteps) ParseScriptStepsJsonIssues(*StepsArr, Steps, Issues);
|
|
509
525
|
else ScriptStepsFromAsset(Script, Steps);
|
|
510
|
-
|
|
511
|
-
bHasMap ? Map : Script->Map, bHasManualOnly ? bManualOnly : Script->bManualOnly, Issues)
|
|
512
|
-
|
|
513
|
-
return Refusal;
|
|
514
|
-
}
|
|
526
|
+
CheckTestScript(Steps, bHasProfile ? Profile : Script->Profile,
|
|
527
|
+
bHasMap ? Map : Script->Map, bHasManualOnly ? bManualOnly : Script->bManualOnly, Issues);
|
|
528
|
+
if (TSharedPtr<FJsonValue> Refusal = RefuseUnsavableScript(Issues, Warnings)) return Refusal;
|
|
515
529
|
}
|
|
516
530
|
|
|
517
531
|
const FString Before = TestScriptStateKey(Script);
|
|
@@ -535,6 +549,7 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptUpdate(const TSharedPtr<FJsonObjec
|
|
|
535
549
|
MCPSetRollback(Result, TEXT("pie_script_update"), Inverse);
|
|
536
550
|
}
|
|
537
551
|
TestScriptFieldsToJson(Script, Path, Result);
|
|
552
|
+
Result->SetArrayField(TEXT("warnings"), ScriptIssuesToJson(Warnings));
|
|
538
553
|
return MCPResult(Result);
|
|
539
554
|
}
|
|
540
555
|
|
|
@@ -551,15 +566,13 @@ TSharedPtr<FJsonValue> FPIEHandlers::PieScriptValidate(const TSharedPtr<FJsonObj
|
|
|
551
566
|
TArray<FScriptStepDef> Steps;
|
|
552
567
|
TArray<FScriptStepIssue> Issues;
|
|
553
568
|
ParseScriptStepsJsonIssues(*StepsArr, Steps, Issues);
|
|
554
|
-
|
|
569
|
+
CheckTestScript(Steps, Profile, Map, bManualOnly, Issues);
|
|
555
570
|
|
|
556
|
-
// The same checks script_create runs,
|
|
571
|
+
// The same checks script_create runs, every finding reported as an error, so a caller can draft in a loop.
|
|
557
572
|
auto Result = MCPSuccess();
|
|
558
|
-
Result->SetBoolField(TEXT("valid"),
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
Result->SetArrayField(TEXT("errors"), Errors);
|
|
562
|
-
if (Refusal.IsValid()) Result->SetStringField(TEXT("summary"), Refusal->AsObject()->GetStringField(TEXT("error")));
|
|
573
|
+
Result->SetBoolField(TEXT("valid"), Issues.Num() == 0);
|
|
574
|
+
Result->SetArrayField(TEXT("errors"), ScriptIssuesToJson(Issues));
|
|
575
|
+
if (Issues.Num() > 0) Result->SetStringField(TEXT("summary"), ScriptIssuesErrorObject(Issues)->GetStringField(TEXT("error")));
|
|
563
576
|
Result->SetArrayField(TEXT("steps"), ScriptStepDefsToJson(Steps));
|
|
564
577
|
return MCPResult(Result);
|
|
565
578
|
}
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEScriptValidation.cpp
CHANGED
|
@@ -550,7 +550,9 @@ namespace MCPPIE
|
|
|
550
550
|
Out.Reset();
|
|
551
551
|
if (Values.Num() == 0)
|
|
552
552
|
{
|
|
553
|
-
|
|
553
|
+
FScriptStepIssue Issue = DraftCheckIssue(INDEX_NONE, TEXT("steps"), TEXT("steps must contain at least one step"));
|
|
554
|
+
Issue.bBlocksSave = true;
|
|
555
|
+
OutIssues.Add(MoveTemp(Issue));
|
|
554
556
|
return false;
|
|
555
557
|
}
|
|
556
558
|
bool bOk = true;
|
|
@@ -565,6 +567,7 @@ namespace MCPPIE
|
|
|
565
567
|
}
|
|
566
568
|
bOk = false;
|
|
567
569
|
FScriptStepIssue Issue = DraftCheckIssue(I, Field, Err);
|
|
570
|
+
Issue.bBlocksSave = true;
|
|
568
571
|
if (Field == TEXT("judge"))
|
|
569
572
|
{
|
|
570
573
|
Issue.Candidates = { TEXT("auto"), TEXT("manual") };
|
|
@@ -1125,21 +1128,29 @@ namespace MCPPIE
|
|
|
1125
1128
|
return O;
|
|
1126
1129
|
}
|
|
1127
1130
|
|
|
1128
|
-
TSharedPtr<
|
|
1131
|
+
TArray<TSharedPtr<FJsonValue>> ScriptIssuesToJson(const TArray<FScriptStepIssue>& Issues)
|
|
1132
|
+
{
|
|
1133
|
+
TArray<TSharedPtr<FJsonValue>> Out;
|
|
1134
|
+
for (const FScriptStepIssue& Issue : Issues) Out.Add(MakeShared<FJsonValueObject>(ScriptIssueToJson(Issue)));
|
|
1135
|
+
return Out;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
TSharedPtr<FJsonObject> ScriptIssuesErrorObject(const TArray<FScriptStepIssue>& Issues, const TCHAR* Lead)
|
|
1129
1139
|
{
|
|
1130
1140
|
TArray<FString> Lines;
|
|
1131
|
-
|
|
1132
|
-
for (const FScriptStepIssue& Issue : Issues)
|
|
1133
|
-
{
|
|
1134
|
-
Lines.Add(Issue.Message);
|
|
1135
|
-
Errors.Add(MakeShared<FJsonValueObject>(ScriptIssueToJson(Issue)));
|
|
1136
|
-
}
|
|
1141
|
+
for (const FScriptStepIssue& Issue : Issues) Lines.Add(Issue.Message);
|
|
1137
1142
|
TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
|
|
1138
1143
|
Obj->SetBoolField(TEXT("success"), false);
|
|
1139
1144
|
Obj->SetStringField(TEXT("errorCode"), TEXT("INVALID_SCRIPT_STEPS"));
|
|
1140
|
-
Obj->SetStringField(TEXT("error"), FString::Printf(TEXT("
|
|
1141
|
-
Issues.Num(), Issues.Num() == 1 ? TEXT("") : TEXT("s"), *FString::Join(Lines, TEXT(" | "))));
|
|
1142
|
-
Obj->SetArrayField(TEXT("errors"),
|
|
1145
|
+
Obj->SetStringField(TEXT("error"), FString::Printf(TEXT("%s (%d problem%s): %s"),
|
|
1146
|
+
Lead, Issues.Num(), Issues.Num() == 1 ? TEXT("") : TEXT("s"), *FString::Join(Lines, TEXT(" | "))));
|
|
1147
|
+
Obj->SetArrayField(TEXT("errors"), ScriptIssuesToJson(Issues));
|
|
1143
1148
|
return Obj;
|
|
1144
1149
|
}
|
|
1150
|
+
|
|
1151
|
+
void SplitScriptIssues(const TArray<FScriptStepIssue>& Issues, TArray<FScriptStepIssue>& OutBlocking,
|
|
1152
|
+
TArray<FScriptStepIssue>& OutWarnings)
|
|
1153
|
+
{
|
|
1154
|
+
for (const FScriptStepIssue& Issue : Issues) (Issue.bBlocksSave ? OutBlocking : OutWarnings).Add(Issue);
|
|
1155
|
+
}
|
|
1145
1156
|
}
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PIE/PIEScriptValidation.h
CHANGED
|
@@ -10,9 +10,10 @@ class UClass;
|
|
|
10
10
|
class UObject;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Draft-time checks for test scripts:
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Draft-time checks for test scripts: a step the observer could never read, or
|
|
14
|
+
* whose op and target cannot hold for the value's type. pie_script_validate
|
|
15
|
+
* reports them; pie_script_create and pie_script_update save anyway and return
|
|
16
|
+
* them as warnings, refusing only what cannot be written (bBlocksSave).
|
|
16
17
|
*
|
|
17
18
|
* Values resolve the way the observer resolves them at run time: a profile
|
|
18
19
|
* tracked value (exact path or last segment), else a dotted reflection path on
|
|
@@ -34,6 +35,9 @@ namespace MCPPIE
|
|
|
34
35
|
FString Field;
|
|
35
36
|
FString Message;
|
|
36
37
|
TArray<FString> Candidates;
|
|
38
|
+
// The script cannot be written with this issue (a step that does not parse, a missing map).
|
|
39
|
+
// Every other issue is a warning: script_create and script_update save with it.
|
|
40
|
+
bool bBlocksSave = false;
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
enum class EScriptValueKind : uint8 { Number, Integer, Boolean, Enum };
|
|
@@ -101,6 +105,13 @@ namespace MCPPIE
|
|
|
101
105
|
TSharedRef<FJsonObject> ScriptWatchableValuesToJson(const FString& TargetClass, const FString& ProfilePath, int32 Limit);
|
|
102
106
|
|
|
103
107
|
TSharedRef<FJsonObject> ScriptIssueToJson(const FScriptStepIssue& Issue);
|
|
108
|
+
TArray<TSharedPtr<FJsonValue>> ScriptIssuesToJson(const TArray<FScriptStepIssue>& Issues);
|
|
104
109
|
// { success: false, errorCode: INVALID_SCRIPT_STEPS, error, errors: [{ step, field, message, candidates? }] }.
|
|
105
|
-
|
|
110
|
+
// Lead opens the error text, ahead of the count and the messages.
|
|
111
|
+
TSharedPtr<FJsonObject> ScriptIssuesErrorObject(const TArray<FScriptStepIssue>& Issues,
|
|
112
|
+
const TCHAR* Lead = TEXT("The script cannot be checked as written"));
|
|
113
|
+
|
|
114
|
+
// Issues that block a save, and the rest (warnings), each in their original order.
|
|
115
|
+
void SplitScriptIssues(const TArray<FScriptStepIssue>& Issues, TArray<FScriptStepIssue>& OutBlocking,
|
|
116
|
+
TArray<FScriptStepIssue>& OutWarnings);
|
|
106
117
|
}
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PIEScriptSaveWarningsTests.cpp
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// pie_script_create and pie_script_update save a script whose steps the static
|
|
2
|
+
// check cannot resolve, returning the findings as warnings, and refuse only a
|
|
3
|
+
// script that cannot be written.
|
|
4
|
+
|
|
5
|
+
#include "Misc/AutomationTest.h"
|
|
6
|
+
|
|
7
|
+
#if WITH_DEV_AUTOMATION_TESTS
|
|
8
|
+
|
|
9
|
+
#include "HandlerRegistry.h"
|
|
10
|
+
#include "HandlerUtils.h"
|
|
11
|
+
#include "Handlers/PIE/PIEHandlers.h"
|
|
12
|
+
#include "Dom/JsonObject.h"
|
|
13
|
+
#include "EditorAssetLibrary.h"
|
|
14
|
+
#include "Misc/Guid.h"
|
|
15
|
+
|
|
16
|
+
namespace MCPPIEScriptSaveWarningsTests
|
|
17
|
+
{
|
|
18
|
+
const TCHAR* const Dir = TEXT("/Game/UEMCPTests/ScriptWarnings");
|
|
19
|
+
// A value only a running game has: the pawn's PlayerState is empty before PIE.
|
|
20
|
+
const TCHAR* const PIEOnlyValue = TEXT("PlayerState.SigilAttributes.Sigil.CurrentValue");
|
|
21
|
+
|
|
22
|
+
TSharedPtr<FJsonObject> Run(FMCPHandlerRegistry& Registry, const TCHAR* Method, const TSharedPtr<FJsonObject>& Request)
|
|
23
|
+
{
|
|
24
|
+
const TSharedPtr<FJsonValue> Response = Registry.ExecuteHandler(Method, Request);
|
|
25
|
+
return Response.IsValid() && Response->Type == EJson::Object ? Response->AsObject() : MakeShared<FJsonObject>();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
bool Succeeded(const TSharedPtr<FJsonObject>& Response)
|
|
29
|
+
{
|
|
30
|
+
bool bSuccess = false;
|
|
31
|
+
return Response->TryGetBoolField(TEXT("success"), bSuccess) && bSuccess;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
FString Field(const TSharedPtr<FJsonObject>& Response, const TCHAR* Name)
|
|
35
|
+
{
|
|
36
|
+
FString Value;
|
|
37
|
+
Response->TryGetStringField(Name, Value);
|
|
38
|
+
return Value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TArray<TSharedPtr<FJsonValue>> Array(const TSharedPtr<FJsonObject>& Response, const TCHAR* Name)
|
|
42
|
+
{
|
|
43
|
+
const TArray<TSharedPtr<FJsonValue>>* Values = nullptr;
|
|
44
|
+
return Response->TryGetArrayField(Name, Values) && Values ? *Values : TArray<TSharedPtr<FJsonValue>>();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
TSharedPtr<FJsonValue> AutoStep(const FString& Value, const FString& Op)
|
|
48
|
+
{
|
|
49
|
+
TSharedPtr<FJsonObject> Expect = MakeShared<FJsonObject>();
|
|
50
|
+
Expect->SetStringField(TEXT("value"), Value);
|
|
51
|
+
Expect->SetStringField(TEXT("op"), Op);
|
|
52
|
+
Expect->SetNumberField(TEXT("target"), 0);
|
|
53
|
+
TSharedPtr<FJsonObject> Step = MakeShared<FJsonObject>();
|
|
54
|
+
Step->SetStringField(TEXT("instruction"), TEXT("The sigil charges"));
|
|
55
|
+
Step->SetObjectField(TEXT("expect"), Expect);
|
|
56
|
+
return MakeShared<FJsonValueObject>(Step);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
TSharedPtr<FJsonValue> ManualStep()
|
|
60
|
+
{
|
|
61
|
+
TSharedPtr<FJsonObject> Step = MakeShared<FJsonObject>();
|
|
62
|
+
Step->SetStringField(TEXT("instruction"), TEXT("The sigil glows"));
|
|
63
|
+
Step->SetStringField(TEXT("judge"), TEXT("manual"));
|
|
64
|
+
return MakeShared<FJsonValueObject>(Step);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The step and field of the first warning, or an empty pair.
|
|
68
|
+
TPair<int32, FString> FirstWarning(const TSharedPtr<FJsonObject>& Response)
|
|
69
|
+
{
|
|
70
|
+
const TArray<TSharedPtr<FJsonValue>> Warnings = Array(Response, TEXT("warnings"));
|
|
71
|
+
if (Warnings.Num() == 0 || !Warnings[0]->AsObject().IsValid()) return { INDEX_NONE, FString() };
|
|
72
|
+
const TSharedPtr<FJsonObject> W = Warnings[0]->AsObject();
|
|
73
|
+
double Step = -1;
|
|
74
|
+
W->TryGetNumberField(TEXT("step"), Step);
|
|
75
|
+
return { static_cast<int32>(Step), Field(W, TEXT("field")) };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
int32 StoredStepCount(FMCPHandlerRegistry& Registry, const FString& Path)
|
|
79
|
+
{
|
|
80
|
+
TSharedPtr<FJsonObject> Read = MakeShared<FJsonObject>();
|
|
81
|
+
Read->SetStringField(TEXT("path"), Path);
|
|
82
|
+
return Array(Run(Registry, TEXT("pie_script_read"), Read), TEXT("steps")).Num();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
87
|
+
FPIEScriptSaveWarningsTest,
|
|
88
|
+
"UE.MCP.PIE.Script.SaveWithWarnings",
|
|
89
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
|
90
|
+
|
|
91
|
+
bool FPIEScriptSaveWarningsTest::RunTest(const FString& /*Parameters*/)
|
|
92
|
+
{
|
|
93
|
+
using namespace MCPPIEScriptSaveWarningsTests;
|
|
94
|
+
|
|
95
|
+
FMCPHandlerRegistry Registry;
|
|
96
|
+
FPIEHandlers::RegisterHandlers(Registry);
|
|
97
|
+
const FString Name = TEXT("S_SaveWarnings_") + FGuid::NewGuid().ToString(EGuidFormats::Digits).Left(8);
|
|
98
|
+
const FString Path = FString(Dir) / Name;
|
|
99
|
+
|
|
100
|
+
// A step the check cannot resolve before PIE is saved, with the finding as a warning.
|
|
101
|
+
TSharedPtr<FJsonObject> Create = MakeShared<FJsonObject>();
|
|
102
|
+
Create->SetStringField(TEXT("name"), Name);
|
|
103
|
+
Create->SetStringField(TEXT("package_path"), Dir);
|
|
104
|
+
Create->SetArrayField(TEXT("steps"), { AutoStep(PIEOnlyValue, TEXT("gt")) });
|
|
105
|
+
const TSharedPtr<FJsonObject> Created = Run(Registry, TEXT("pie_script_create"), Create);
|
|
106
|
+
TestTrue(FString::Printf(TEXT("script_create saves despite the finding: %s"), *Field(Created, TEXT("error"))), Succeeded(Created));
|
|
107
|
+
TestTrue(TEXT("script_create reports created"), Created->HasTypedField<EJson::Boolean>(TEXT("created")));
|
|
108
|
+
TestEqual(TEXT("the warning names step 0"), FirstWarning(Created).Key, 0);
|
|
109
|
+
TestEqual(TEXT("the warning names expect.value"), FirstWarning(Created).Value, FString(TEXT("expect.value")));
|
|
110
|
+
TestTrue(TEXT("the asset exists"), UEditorAssetLibrary::DoesAssetExist(Path));
|
|
111
|
+
|
|
112
|
+
TSharedPtr<FJsonObject> Read = MakeShared<FJsonObject>();
|
|
113
|
+
Read->SetStringField(TEXT("path"), Path);
|
|
114
|
+
const TArray<TSharedPtr<FJsonValue>> Stored = Array(Run(Registry, TEXT("pie_script_read"), Read), TEXT("steps"));
|
|
115
|
+
if (TestEqual(TEXT("one step stored"), Stored.Num(), 1))
|
|
116
|
+
{
|
|
117
|
+
const TSharedPtr<FJsonObject>* Expect = nullptr;
|
|
118
|
+
Stored[0]->AsObject()->TryGetObjectField(TEXT("expect"), Expect);
|
|
119
|
+
TestEqual(TEXT("the unresolved value is stored as written"), Expect ? Field(*Expect, TEXT("value")) : FString(), FString(PIEOnlyValue));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Editing a script that still has the finding saves, and keeps warning.
|
|
123
|
+
TSharedPtr<FJsonObject> Grow = MakeShared<FJsonObject>();
|
|
124
|
+
Grow->SetStringField(TEXT("path"), Path);
|
|
125
|
+
Grow->SetArrayField(TEXT("steps"), { AutoStep(PIEOnlyValue, TEXT("gt")), ManualStep() });
|
|
126
|
+
const TSharedPtr<FJsonObject> Grown = Run(Registry, TEXT("pie_script_update"), Grow);
|
|
127
|
+
TestTrue(FString::Printf(TEXT("script_update saves despite the finding: %s"), *Field(Grown, TEXT("error"))), Succeeded(Grown));
|
|
128
|
+
TestEqual(TEXT("script_update warns about step 0"), FirstWarning(Grown).Key, 0);
|
|
129
|
+
TestEqual(TEXT("two steps stored"), StoredStepCount(Registry, Path), 2);
|
|
130
|
+
|
|
131
|
+
// Deleting a step saves too.
|
|
132
|
+
TSharedPtr<FJsonObject> Shrink = MakeShared<FJsonObject>();
|
|
133
|
+
Shrink->SetStringField(TEXT("path"), Path);
|
|
134
|
+
Shrink->SetArrayField(TEXT("steps"), { AutoStep(PIEOnlyValue, TEXT("gt")) });
|
|
135
|
+
TestTrue(TEXT("script_update removing a step saves"), Succeeded(Run(Registry, TEXT("pie_script_update"), Shrink)));
|
|
136
|
+
TestEqual(TEXT("one step stored after the removal"), StoredStepCount(Registry, Path), 1);
|
|
137
|
+
|
|
138
|
+
// A step that does not parse cannot be written: refused, nothing stored changes.
|
|
139
|
+
TSharedPtr<FJsonObject> Broken = MakeShared<FJsonObject>();
|
|
140
|
+
Broken->SetStringField(TEXT("path"), Path);
|
|
141
|
+
Broken->SetArrayField(TEXT("steps"), { AutoStep(PIEOnlyValue, TEXT("gt")), AutoStep(TEXT("BaseEyeHeight"), TEXT("sideways")) });
|
|
142
|
+
const TSharedPtr<FJsonObject> Refused = Run(Registry, TEXT("pie_script_update"), Broken);
|
|
143
|
+
TestFalse(TEXT("an unparseable step is refused"), Succeeded(Refused));
|
|
144
|
+
TestEqual(TEXT("refusal code"), Field(Refused, TEXT("errorCode")), FString(TEXT("INVALID_SCRIPT_STEPS")));
|
|
145
|
+
const TArray<TSharedPtr<FJsonValue>> Errors = Array(Refused, TEXT("errors"));
|
|
146
|
+
if (TestEqual(TEXT("one blocking error"), Errors.Num(), 1))
|
|
147
|
+
{
|
|
148
|
+
TestEqual(TEXT("the error names expect.op"), Field(Errors[0]->AsObject(), TEXT("field")), FString(TEXT("expect.op")));
|
|
149
|
+
}
|
|
150
|
+
TestEqual(TEXT("the refusal still lists the warning"), FirstWarning(Refused).Key, 0);
|
|
151
|
+
TestEqual(TEXT("the refused update stored nothing"), StoredStepCount(Registry, Path), 1);
|
|
152
|
+
|
|
153
|
+
// Nothing to save at all is refused.
|
|
154
|
+
TSharedPtr<FJsonObject> Empty = MakeShared<FJsonObject>();
|
|
155
|
+
Empty->SetStringField(TEXT("path"), Path);
|
|
156
|
+
Empty->SetArrayField(TEXT("steps"), {});
|
|
157
|
+
TestFalse(TEXT("an empty steps list is refused"), Succeeded(Run(Registry, TEXT("pie_script_update"), Empty)));
|
|
158
|
+
|
|
159
|
+
UEditorAssetLibrary::DeleteAsset(Path);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#endif // WITH_DEV_AUTOMATION_TESTS
|
package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PIEScriptValidationTests.cpp
CHANGED
|
@@ -126,6 +126,8 @@ bool FPIEScriptDraftParseTest::RunTest(const FString& /*Parameters*/)
|
|
|
126
126
|
"{\"instruction\":\"x\",\"resolve_in_pie\":true},"
|
|
127
127
|
"{\"instruction\":\"x\",\"resolve_in_pie\":\"yes\",\"expect\":{\"value\":\"A\",\"op\":\"exists\"}}]")), Steps, Issues);
|
|
128
128
|
TestFalse(TEXT("bad steps refused"), bOk);
|
|
129
|
+
TestTrue(TEXT("a step that does not parse blocks a save"),
|
|
130
|
+
Issues.Num() > 0 && Issues.FilterByPredicate([](const FScriptStepIssue& I) { return !I.bBlocksSave; }).Num() == 0);
|
|
129
131
|
TestEqual(TEXT("indices line up with the input"), Steps.Num(), 5);
|
|
130
132
|
TestEqual(TEXT("one issue per bad step"), Issues.Num(), 4);
|
|
131
133
|
TestTrue(TEXT("unknown op names its field"), HasIssue(Issues, 1, TEXT("expect.op")));
|
|
@@ -138,6 +140,7 @@ bool FPIEScriptDraftParseTest::RunTest(const FString& /*Parameters*/)
|
|
|
138
140
|
Issues.Reset();
|
|
139
141
|
TestFalse(TEXT("empty steps refused"), ParseScriptStepsJsonIssues(TArray<TSharedPtr<FJsonValue>>(), Steps, Issues));
|
|
140
142
|
TestTrue(TEXT("empty steps is a script-level issue"), HasIssue(Issues, INDEX_NONE, TEXT("steps")));
|
|
143
|
+
TestTrue(TEXT("empty steps blocks a save"), Issues.Num() > 0 && Issues.Last().bBlocksSave);
|
|
141
144
|
|
|
142
145
|
// resolve_in_pie and a boolean target survive a round trip through JSON.
|
|
143
146
|
Issues.Reset();
|
|
@@ -273,6 +276,10 @@ bool FPIEScriptDraftRefusalTest::RunTest(const FString& /*Parameters*/)
|
|
|
273
276
|
|
|
274
277
|
TArray<FScriptStepIssue> Issues = Check({ Auto(EScriptExpectOp::Gt, TEXT("Helth"), true, 0.0) });
|
|
275
278
|
Issues.Append(Check({ Manual() }));
|
|
279
|
+
TArray<FScriptStepIssue> Blocking, Warnings;
|
|
280
|
+
SplitScriptIssues(Issues, Blocking, Warnings);
|
|
281
|
+
TestEqual(TEXT("an unresolved value and no auto step do not block a save"), Blocking.Num(), 0);
|
|
282
|
+
TestEqual(TEXT("both are warnings"), Warnings.Num(), 2);
|
|
276
283
|
const TSharedPtr<FJsonObject> Obj = ScriptIssuesErrorObject(Issues);
|
|
277
284
|
bool bSuccess = true;
|
|
278
285
|
TestTrue(TEXT("success false"), Obj->TryGetBoolField(TEXT("success"), bSuccess) && !bSuccess);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"FileVersion": 3,
|
|
3
3
|
"Version": 1,
|
|
4
|
-
"VersionName": "1.4.11-beta.
|
|
4
|
+
"VersionName": "1.4.11-beta.8",
|
|
5
5
|
"FriendlyName": "UE MCP Bridge",
|
|
6
6
|
"Description": "C++ WebSocket bridge for UE-MCP, providing 2042+ editor actions over the MCP protocol",
|
|
7
7
|
"Category": "Developer Tools",
|