ue-mcp 1.0.37 → 1.0.39
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/README.md +2 -2
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +1 -1
- package/dist/pie/schema.d.ts +10 -10
- package/dist/tool-counts.json +6 -6
- package/dist/tools/animation.js +3 -0
- package/dist/tools/animation.js.map +1 -1
- package/dist/tools/gameplay.js +21 -0
- package/dist/tools/gameplay.js.map +1 -1
- package/dist/ue-mcp.default.yml +72 -0
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerUtils.h +13 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +2 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +1 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +127 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +11 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +13 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_PIEObserve.cpp +495 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_PIERecord.cpp +0 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/ReflectionHandlers.cpp +236 -92
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/MCPObservationProfile.h +59 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/PIEInputRecorder.cpp +0 -7
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/PIEInputReplayer.cpp +0 -8
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/PIEObserver.cpp +379 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/PIEObserver.h +109 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/PIE/PIESequenceFormat.h +5 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +5 -1
|
@@ -36,6 +36,190 @@ void FReflectionHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
36
36
|
Registry.RegisterHandler(TEXT("set_enum_entries"), &SetEnumEntries);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
namespace
|
|
40
|
+
{
|
|
41
|
+
/**
|
|
42
|
+
* UHT compiles `///` doc comments into ToolTip metadata at build time.
|
|
43
|
+
* Returns the ToolTip if present, otherwise an empty string. Caller is
|
|
44
|
+
* responsible for omitting empty values from the JSON payload.
|
|
45
|
+
*/
|
|
46
|
+
FString ReadTooltip(const UField* Field)
|
|
47
|
+
{
|
|
48
|
+
#if WITH_EDITOR
|
|
49
|
+
if (!Field) return FString();
|
|
50
|
+
// UField::GetMetaData("ToolTip") returns the raw text. Trimming
|
|
51
|
+
// matches what the editor's tooltip panel does.
|
|
52
|
+
const FString Raw = Field->GetMetaData(TEXT("ToolTip"));
|
|
53
|
+
return Raw.TrimStartAndEnd();
|
|
54
|
+
#else
|
|
55
|
+
return FString();
|
|
56
|
+
#endif
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
FString ReadPropertyTooltip(const FProperty* Prop)
|
|
60
|
+
{
|
|
61
|
+
#if WITH_EDITOR
|
|
62
|
+
if (!Prop) return FString();
|
|
63
|
+
return Prop->GetMetaData(TEXT("ToolTip")).TrimStartAndEnd();
|
|
64
|
+
#else
|
|
65
|
+
return FString();
|
|
66
|
+
#endif
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void AddIfNonEmpty(TSharedPtr<FJsonObject> Obj, const TCHAR* Key, const FString& Value)
|
|
70
|
+
{
|
|
71
|
+
if (!Value.IsEmpty())
|
|
72
|
+
{
|
|
73
|
+
Obj->SetStringField(Key, Value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Encode the common EPropertyFlags bits the editor surfaces in its
|
|
79
|
+
* property panel + Blueprint nodes. Skips internal bookkeeping flags
|
|
80
|
+
* (CPF_NativeAccessSpecifier*, CPF_PersistentInstance, etc.) that aren't
|
|
81
|
+
* useful to AI callers reasoning about how to use a property.
|
|
82
|
+
*/
|
|
83
|
+
TArray<TSharedPtr<FJsonValue>> EncodePropertyFlags(const FProperty* Prop)
|
|
84
|
+
{
|
|
85
|
+
TArray<TSharedPtr<FJsonValue>> Out;
|
|
86
|
+
if (!Prop) return Out;
|
|
87
|
+
const EPropertyFlags F = Prop->PropertyFlags;
|
|
88
|
+
auto Push = [&Out](const TCHAR* Name) { Out.Add(MakeShared<FJsonValueString>(Name)); };
|
|
89
|
+
|
|
90
|
+
if (F & CPF_Edit)
|
|
91
|
+
{
|
|
92
|
+
if (F & CPF_DisableEditOnInstance) Push(TEXT("EditDefaultsOnly"));
|
|
93
|
+
else if (F & CPF_DisableEditOnTemplate) Push(TEXT("EditInstanceOnly"));
|
|
94
|
+
else Push(TEXT("EditAnywhere"));
|
|
95
|
+
}
|
|
96
|
+
if (F & CPF_EditConst) Push(TEXT("VisibleAnywhere"));
|
|
97
|
+
if (F & CPF_BlueprintVisible)
|
|
98
|
+
{
|
|
99
|
+
if (F & CPF_BlueprintReadOnly) Push(TEXT("BlueprintReadOnly"));
|
|
100
|
+
else Push(TEXT("BlueprintReadWrite"));
|
|
101
|
+
}
|
|
102
|
+
if (F & CPF_Net) Push(TEXT("Replicated"));
|
|
103
|
+
if (F & CPF_RepNotify) Push(TEXT("RepNotify"));
|
|
104
|
+
if (F & CPF_Transient) Push(TEXT("Transient"));
|
|
105
|
+
if (F & CPF_Config) Push(TEXT("Config"));
|
|
106
|
+
if (F & CPF_GlobalConfig) Push(TEXT("GlobalConfig"));
|
|
107
|
+
if (F & CPF_SaveGame) Push(TEXT("SaveGame"));
|
|
108
|
+
if (F & CPF_Interp) Push(TEXT("Interp"));
|
|
109
|
+
if (F & CPF_AdvancedDisplay) Push(TEXT("AdvancedDisplay"));
|
|
110
|
+
if (F & CPF_Deprecated) Push(TEXT("Deprecated"));
|
|
111
|
+
if (F & CPF_NoClear) Push(TEXT("NoClear"));
|
|
112
|
+
if (F & CPF_ExposeOnSpawn) Push(TEXT("ExposeOnSpawn"));
|
|
113
|
+
return Out;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Function flags the editor cares about: how Blueprints can call it, how
|
|
118
|
+
* the network treats it, and whether it's an Exec console command.
|
|
119
|
+
*/
|
|
120
|
+
TArray<TSharedPtr<FJsonValue>> EncodeFunctionFlags(const UFunction* Func)
|
|
121
|
+
{
|
|
122
|
+
TArray<TSharedPtr<FJsonValue>> Out;
|
|
123
|
+
if (!Func) return Out;
|
|
124
|
+
const EFunctionFlags F = Func->FunctionFlags;
|
|
125
|
+
auto Push = [&Out](const TCHAR* Name) { Out.Add(MakeShared<FJsonValueString>(Name)); };
|
|
126
|
+
|
|
127
|
+
if (F & FUNC_BlueprintCallable) Push(TEXT("BlueprintCallable"));
|
|
128
|
+
if (F & FUNC_BlueprintEvent) Push(TEXT("BlueprintImplementableEvent"));
|
|
129
|
+
if (F & FUNC_BlueprintPure) Push(TEXT("BlueprintPure"));
|
|
130
|
+
if (F & FUNC_Exec) Push(TEXT("Exec"));
|
|
131
|
+
if (F & FUNC_NetServer) Push(TEXT("Server"));
|
|
132
|
+
if (F & FUNC_NetClient) Push(TEXT("Client"));
|
|
133
|
+
if (F & FUNC_NetMulticast) Push(TEXT("NetMulticast"));
|
|
134
|
+
if (F & FUNC_NetReliable) Push(TEXT("Reliable"));
|
|
135
|
+
if (F & FUNC_Static) Push(TEXT("Static"));
|
|
136
|
+
return Out;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build the per-property JSON block surfaced under a class/struct's
|
|
141
|
+
* `properties:` / `fields:` array. Includes name + type (the old
|
|
142
|
+
* contract) plus all metadata the editor exposes: tooltip, category,
|
|
143
|
+
* display name, edit-condition predicate, clamp range, and the flag
|
|
144
|
+
* names a UHEADER author would have typed.
|
|
145
|
+
*/
|
|
146
|
+
TSharedPtr<FJsonObject> SerializePropertyMeta(FProperty* Prop)
|
|
147
|
+
{
|
|
148
|
+
TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
|
|
149
|
+
Obj->SetStringField(TEXT("name"), Prop->GetName());
|
|
150
|
+
Obj->SetStringField(TEXT("type"), Prop->GetCPPType());
|
|
151
|
+
#if WITH_EDITOR
|
|
152
|
+
AddIfNonEmpty(Obj, TEXT("tooltip"), ReadPropertyTooltip(Prop));
|
|
153
|
+
AddIfNonEmpty(Obj, TEXT("category"), Prop->GetMetaData(TEXT("Category")));
|
|
154
|
+
AddIfNonEmpty(Obj, TEXT("displayName"), Prop->GetMetaData(TEXT("DisplayName")));
|
|
155
|
+
AddIfNonEmpty(Obj, TEXT("editCondition"), Prop->GetMetaData(TEXT("EditCondition")));
|
|
156
|
+
AddIfNonEmpty(Obj, TEXT("clampMin"), Prop->GetMetaData(TEXT("ClampMin")));
|
|
157
|
+
AddIfNonEmpty(Obj, TEXT("clampMax"), Prop->GetMetaData(TEXT("ClampMax")));
|
|
158
|
+
AddIfNonEmpty(Obj, TEXT("uiMin"), Prop->GetMetaData(TEXT("UIMin")));
|
|
159
|
+
AddIfNonEmpty(Obj, TEXT("uiMax"), Prop->GetMetaData(TEXT("UIMax")));
|
|
160
|
+
AddIfNonEmpty(Obj, TEXT("units"), Prop->GetMetaData(TEXT("Units")));
|
|
161
|
+
#endif
|
|
162
|
+
TArray<TSharedPtr<FJsonValue>> Flags = EncodePropertyFlags(Prop);
|
|
163
|
+
if (Flags.Num() > 0)
|
|
164
|
+
{
|
|
165
|
+
Obj->SetArrayField(TEXT("flags"), Flags);
|
|
166
|
+
}
|
|
167
|
+
return Obj;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build the per-function JSON block surfaced under a class's
|
|
172
|
+
* `functions:` array. Returns a structured params array (each with
|
|
173
|
+
* name + type + optional out/ref markers) and a separate returnType
|
|
174
|
+
* string when the function has a CPF_ReturnParm property.
|
|
175
|
+
*/
|
|
176
|
+
TSharedPtr<FJsonObject> SerializeFunctionMeta(UFunction* Func)
|
|
177
|
+
{
|
|
178
|
+
TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>();
|
|
179
|
+
Obj->SetStringField(TEXT("name"), Func->GetName());
|
|
180
|
+
#if WITH_EDITOR
|
|
181
|
+
AddIfNonEmpty(Obj, TEXT("tooltip"), ReadTooltip(Func));
|
|
182
|
+
AddIfNonEmpty(Obj, TEXT("category"), Func->GetMetaData(TEXT("Category")));
|
|
183
|
+
AddIfNonEmpty(Obj, TEXT("displayName"), Func->GetMetaData(TEXT("DisplayName")));
|
|
184
|
+
AddIfNonEmpty(Obj, TEXT("keywords"), Func->GetMetaData(TEXT("Keywords")));
|
|
185
|
+
#endif
|
|
186
|
+
|
|
187
|
+
TArray<TSharedPtr<FJsonValue>> Params;
|
|
188
|
+
FString ReturnType;
|
|
189
|
+
for (TFieldIterator<FProperty> PIt(Func); PIt; ++PIt)
|
|
190
|
+
{
|
|
191
|
+
FProperty* P = *PIt;
|
|
192
|
+
if (P->PropertyFlags & CPF_ReturnParm)
|
|
193
|
+
{
|
|
194
|
+
ReturnType = P->GetCPPType();
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
TSharedPtr<FJsonObject> ParamObj = MakeShared<FJsonObject>();
|
|
198
|
+
ParamObj->SetStringField(TEXT("name"), P->GetName());
|
|
199
|
+
ParamObj->SetStringField(TEXT("type"), P->GetCPPType());
|
|
200
|
+
if (P->PropertyFlags & CPF_OutParm) ParamObj->SetBoolField(TEXT("out"), true);
|
|
201
|
+
if (P->PropertyFlags & CPF_ReferenceParm) ParamObj->SetBoolField(TEXT("byRef"), true);
|
|
202
|
+
Params.Add(MakeShared<FJsonValueObject>(ParamObj));
|
|
203
|
+
}
|
|
204
|
+
Obj->SetArrayField(TEXT("params"), Params);
|
|
205
|
+
if (!ReturnType.IsEmpty())
|
|
206
|
+
{
|
|
207
|
+
Obj->SetStringField(TEXT("returnType"), ReturnType);
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{
|
|
211
|
+
Obj->SetStringField(TEXT("returnType"), TEXT("void"));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
TArray<TSharedPtr<FJsonValue>> Flags = EncodeFunctionFlags(Func);
|
|
215
|
+
if (Flags.Num() > 0)
|
|
216
|
+
{
|
|
217
|
+
Obj->SetArrayField(TEXT("flags"), Flags);
|
|
218
|
+
}
|
|
219
|
+
return Obj;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
39
223
|
TSharedPtr<FJsonValue> FReflectionHandlers::ReflectClass(const TSharedPtr<FJsonObject>& Params)
|
|
40
224
|
{
|
|
41
225
|
FString ClassName;
|
|
@@ -51,6 +235,11 @@ TSharedPtr<FJsonValue> FReflectionHandlers::ReflectClass(const TSharedPtr<FJsonO
|
|
|
51
235
|
|
|
52
236
|
auto Result = MCPSuccess();
|
|
53
237
|
Result->SetStringField(TEXT("className"), Class->GetName());
|
|
238
|
+
AddIfNonEmpty(Result, TEXT("tooltip"), ReadTooltip(Class));
|
|
239
|
+
#if WITH_EDITOR
|
|
240
|
+
AddIfNonEmpty(Result, TEXT("displayName"), Class->GetMetaData(TEXT("DisplayName")));
|
|
241
|
+
AddIfNonEmpty(Result, TEXT("category"), Class->GetMetaData(TEXT("Category")));
|
|
242
|
+
#endif
|
|
54
243
|
|
|
55
244
|
if (Class->GetSuperClass())
|
|
56
245
|
{
|
|
@@ -69,27 +258,22 @@ TSharedPtr<FJsonValue> FReflectionHandlers::ReflectClass(const TSharedPtr<FJsonO
|
|
|
69
258
|
|
|
70
259
|
Result->SetBoolField(TEXT("isAbstract"), Class->HasAnyClassFlags(CLASS_Abstract));
|
|
71
260
|
|
|
72
|
-
//
|
|
261
|
+
// Properties: name + type plus every metadata field the editor's property
|
|
262
|
+
// panel renders - tooltip text, category, clamps, replication flags, etc.
|
|
73
263
|
TArray<TSharedPtr<FJsonValue>> PropertiesArray;
|
|
74
264
|
for (TFieldIterator<FProperty> PropIt(Class, bIncludeInherited ? EFieldIteratorFlags::IncludeSuper : EFieldIteratorFlags::ExcludeSuper); PropIt; ++PropIt)
|
|
75
265
|
{
|
|
76
|
-
|
|
77
|
-
TSharedPtr<FJsonObject> PropObj = MakeShared<FJsonObject>();
|
|
78
|
-
PropObj->SetStringField(TEXT("name"), Prop->GetName());
|
|
79
|
-
PropObj->SetStringField(TEXT("type"), Prop->GetCPPType());
|
|
80
|
-
PropertiesArray.Add(MakeShared<FJsonValueObject>(PropObj));
|
|
266
|
+
PropertiesArray.Add(MakeShared<FJsonValueObject>(SerializePropertyMeta(*PropIt)));
|
|
81
267
|
}
|
|
82
268
|
Result->SetArrayField(TEXT("properties"), PropertiesArray);
|
|
83
269
|
Result->SetNumberField(TEXT("propertyCount"), PropertiesArray.Num());
|
|
84
270
|
|
|
85
|
-
//
|
|
271
|
+
// Functions: structured params + return type + tooltip + flags so callers
|
|
272
|
+
// can see how to invoke each function without grepping the header.
|
|
86
273
|
TArray<TSharedPtr<FJsonValue>> FunctionsArray;
|
|
87
274
|
for (TFieldIterator<UFunction> FuncIt(Class, bIncludeInherited ? EFieldIteratorFlags::IncludeSuper : EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
|
|
88
275
|
{
|
|
89
|
-
|
|
90
|
-
TSharedPtr<FJsonObject> FuncObj = MakeShared<FJsonObject>();
|
|
91
|
-
FuncObj->SetStringField(TEXT("name"), Func->GetName());
|
|
92
|
-
FunctionsArray.Add(MakeShared<FJsonValueObject>(FuncObj));
|
|
276
|
+
FunctionsArray.Add(MakeShared<FJsonValueObject>(SerializeFunctionMeta(*FuncIt)));
|
|
93
277
|
}
|
|
94
278
|
Result->SetArrayField(TEXT("functions"), FunctionsArray);
|
|
95
279
|
Result->SetNumberField(TEXT("functionCount"), FunctionsArray.Num());
|
|
@@ -110,15 +294,15 @@ TSharedPtr<FJsonValue> FReflectionHandlers::ReflectStruct(const TSharedPtr<FJson
|
|
|
110
294
|
|
|
111
295
|
auto Result = MCPSuccess();
|
|
112
296
|
Result->SetStringField(TEXT("structName"), Struct->GetName());
|
|
297
|
+
AddIfNonEmpty(Result, TEXT("tooltip"), ReadTooltip(Struct));
|
|
298
|
+
#if WITH_EDITOR
|
|
299
|
+
AddIfNonEmpty(Result, TEXT("displayName"), Struct->GetMetaData(TEXT("DisplayName")));
|
|
300
|
+
#endif
|
|
113
301
|
|
|
114
302
|
TArray<TSharedPtr<FJsonValue>> FieldsArray;
|
|
115
303
|
for (TFieldIterator<FProperty> PropIt(Struct); PropIt; ++PropIt)
|
|
116
304
|
{
|
|
117
|
-
|
|
118
|
-
TSharedPtr<FJsonObject> FieldObj = MakeShared<FJsonObject>();
|
|
119
|
-
FieldObj->SetStringField(TEXT("name"), Prop->GetName());
|
|
120
|
-
FieldObj->SetStringField(TEXT("type"), Prop->GetCPPType());
|
|
121
|
-
FieldsArray.Add(MakeShared<FJsonValueObject>(FieldObj));
|
|
305
|
+
FieldsArray.Add(MakeShared<FJsonValueObject>(SerializePropertyMeta(*PropIt)));
|
|
122
306
|
}
|
|
123
307
|
Result->SetArrayField(TEXT("fields"), FieldsArray);
|
|
124
308
|
Result->SetNumberField(TEXT("fieldCount"), FieldsArray.Num());
|
|
@@ -139,6 +323,7 @@ TSharedPtr<FJsonValue> FReflectionHandlers::ReflectEnum(const TSharedPtr<FJsonOb
|
|
|
139
323
|
|
|
140
324
|
auto Result = MCPSuccess();
|
|
141
325
|
Result->SetStringField(TEXT("enumName"), Enum->GetName());
|
|
326
|
+
AddIfNonEmpty(Result, TEXT("tooltip"), ReadTooltip(Enum));
|
|
142
327
|
|
|
143
328
|
TArray<TSharedPtr<FJsonValue>> ValuesArray;
|
|
144
329
|
int32 NumEnums = Enum->NumEnums();
|
|
@@ -151,6 +336,13 @@ TSharedPtr<FJsonValue> FReflectionHandlers::ReflectEnum(const TSharedPtr<FJsonOb
|
|
|
151
336
|
ValueObj->SetStringField(TEXT("name"), EnumNameStr);
|
|
152
337
|
ValueObj->SetNumberField(TEXT("value"), Enum->GetValueByIndex(i));
|
|
153
338
|
ValueObj->SetStringField(TEXT("displayName"), Enum->GetDisplayNameTextByIndex(i).ToString());
|
|
339
|
+
#if WITH_EDITOR
|
|
340
|
+
// Per-value tooltip - UEnum stores ToolTip metadata per enum
|
|
341
|
+
// entry, addressable by index. Editor uses the same call to
|
|
342
|
+
// populate the dropdown tooltips in Details panels.
|
|
343
|
+
FString ValueTooltip = Enum->GetMetaData(TEXT("ToolTip"), i).TrimStartAndEnd();
|
|
344
|
+
AddIfNonEmpty(ValueObj, TEXT("tooltip"), ValueTooltip);
|
|
345
|
+
#endif
|
|
154
346
|
ValuesArray.Add(MakeShared<FJsonValueObject>(ValueObj));
|
|
155
347
|
}
|
|
156
348
|
}
|
|
@@ -315,34 +507,23 @@ TSharedPtr<FJsonValue> FReflectionHandlers::CreateGameplayTag(const TSharedPtr<F
|
|
|
315
507
|
|
|
316
508
|
UClass* FReflectionHandlers::FindClass(const FString& ClassName)
|
|
317
509
|
{
|
|
318
|
-
//
|
|
510
|
+
// Full-path lookup first: /Script/Engine.Actor, /Script/Foo.UBar, etc.
|
|
319
511
|
UClass* Class = FindObject<UClass>(nullptr, *ClassName);
|
|
320
512
|
if (Class)
|
|
321
513
|
{
|
|
322
514
|
return Class;
|
|
323
515
|
}
|
|
324
516
|
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Try with common prefixes
|
|
334
|
-
TArray<FString> Prefixes = { TEXT(""), TEXT("A"), TEXT("U"), TEXT("F") };
|
|
517
|
+
// Short-name lookup. In UE 5.6+, FindObject(nullptr, "Actor") no longer
|
|
518
|
+
// scans every package - it only finds top-level objects. FindFirstObject
|
|
519
|
+
// is the replacement that walks all loaded UClass objects by leaf name.
|
|
520
|
+
// Try the caller's spelling, then common UE prefixes (A/U/F) that agents
|
|
521
|
+
// often drop when typing class names.
|
|
522
|
+
const TArray<FString> Prefixes = { TEXT(""), TEXT("A"), TEXT("U"), TEXT("F") };
|
|
335
523
|
for (const FString& Prefix : Prefixes)
|
|
336
524
|
{
|
|
337
|
-
FString
|
|
338
|
-
Class =
|
|
339
|
-
if (Class)
|
|
340
|
-
{
|
|
341
|
-
return Class;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
FString CandidatePath = FString::Printf(TEXT("/Script/%s"), *CandidateName);
|
|
345
|
-
Class = FindObject<UClass>(nullptr, *CandidatePath);
|
|
525
|
+
const FString Candidate = Prefix + ClassName;
|
|
526
|
+
Class = FindFirstObject<UClass>(*Candidate, EFindFirstObjectOptions::NativeFirst);
|
|
346
527
|
if (Class)
|
|
347
528
|
{
|
|
348
529
|
return Class;
|
|
@@ -361,43 +542,17 @@ UScriptStruct* FReflectionHandlers::FindStruct(const FString& StructName)
|
|
|
361
542
|
return Struct;
|
|
362
543
|
}
|
|
363
544
|
|
|
364
|
-
//
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
545
|
+
// Short-name lookup via FindFirstObject (UE 5.6+ replacement for the
|
|
546
|
+
// "any package" FindObject pattern). Tries the caller's spelling first,
|
|
547
|
+
// then F-prefixed - matches the convention agents typically use ("Vector"
|
|
548
|
+
// vs "FVector").
|
|
549
|
+
const TArray<FString> Candidates = { StructName, TEXT("F") + StructName };
|
|
550
|
+
for (const FString& Candidate : Candidates)
|
|
368
551
|
{
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
// Try with F prefix
|
|
373
|
-
FString FName = TEXT("F") + StructName;
|
|
374
|
-
Struct = FindObject<UScriptStruct>(nullptr, *FName);
|
|
375
|
-
if (Struct)
|
|
376
|
-
{
|
|
377
|
-
return Struct;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
FString FPath = FString::Printf(TEXT("/Script/%s"), *FName);
|
|
381
|
-
Struct = FindObject<UScriptStruct>(nullptr, *FPath);
|
|
382
|
-
if (Struct)
|
|
383
|
-
{
|
|
384
|
-
return Struct;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Iterate all loaded UScriptStruct objects to find by short name
|
|
388
|
-
// This catches project-defined structs in any module (e.g. /Script/MyModule.FMyStruct)
|
|
389
|
-
FString NameToFind = StructName;
|
|
390
|
-
FString FNameToFind = FName;
|
|
391
|
-
for (TObjectIterator<UScriptStruct> It; It; ++It)
|
|
392
|
-
{
|
|
393
|
-
UScriptStruct* Current = *It;
|
|
394
|
-
if (Current)
|
|
552
|
+
Struct = FindFirstObject<UScriptStruct>(*Candidate, EFindFirstObjectOptions::NativeFirst);
|
|
553
|
+
if (Struct)
|
|
395
554
|
{
|
|
396
|
-
|
|
397
|
-
if (CurrentName == NameToFind || CurrentName == FNameToFind)
|
|
398
|
-
{
|
|
399
|
-
return Current;
|
|
400
|
-
}
|
|
555
|
+
return Struct;
|
|
401
556
|
}
|
|
402
557
|
}
|
|
403
558
|
|
|
@@ -406,34 +561,23 @@ UScriptStruct* FReflectionHandlers::FindStruct(const FString& StructName)
|
|
|
406
561
|
|
|
407
562
|
UEnum* FReflectionHandlers::FindEnum(const FString& EnumName)
|
|
408
563
|
{
|
|
409
|
-
//
|
|
564
|
+
// Full-path lookup first: /Script/Engine.ECollisionChannel, etc.
|
|
410
565
|
UEnum* Enum = FindObject<UEnum>(nullptr, *EnumName);
|
|
411
566
|
if (Enum)
|
|
412
567
|
{
|
|
413
568
|
return Enum;
|
|
414
569
|
}
|
|
415
570
|
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
{
|
|
421
|
-
return Enum;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Try with E prefix
|
|
425
|
-
FString EName = TEXT("E") + EnumName;
|
|
426
|
-
Enum = FindObject<UEnum>(nullptr, *EName);
|
|
427
|
-
if (Enum)
|
|
571
|
+
// Short-name lookup via FindFirstObject (UE 5.6+ replacement). Tries
|
|
572
|
+
// the caller's spelling, then E-prefixed - the standard UE convention.
|
|
573
|
+
const TArray<FString> Candidates = { EnumName, TEXT("E") + EnumName };
|
|
574
|
+
for (const FString& Candidate : Candidates)
|
|
428
575
|
{
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (Enum)
|
|
435
|
-
{
|
|
436
|
-
return Enum;
|
|
576
|
+
Enum = FindFirstObject<UEnum>(*Candidate, EFindFirstObjectOptions::NativeFirst);
|
|
577
|
+
if (Enum)
|
|
578
|
+
{
|
|
579
|
+
return Enum;
|
|
580
|
+
}
|
|
437
581
|
}
|
|
438
582
|
|
|
439
583
|
return nullptr;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "CoreMinimal.h"
|
|
4
|
+
#include "Engine/DataAsset.h"
|
|
5
|
+
#include "MCPObservationProfile.generated.h"
|
|
6
|
+
|
|
7
|
+
USTRUCT(BlueprintType)
|
|
8
|
+
struct FMCPTrackedValueEntry
|
|
9
|
+
{
|
|
10
|
+
GENERATED_BODY()
|
|
11
|
+
|
|
12
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tracking")
|
|
13
|
+
FString Path;
|
|
14
|
+
|
|
15
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tracking", meta=(ClampMin="0"))
|
|
16
|
+
float DriftThreshold = 0.f;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
USTRUCT(BlueprintType)
|
|
20
|
+
struct FMCPTrackedActorEntry
|
|
21
|
+
{
|
|
22
|
+
GENERATED_BODY()
|
|
23
|
+
|
|
24
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tracking")
|
|
25
|
+
FString ActorId;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
UCLASS(BlueprintType)
|
|
29
|
+
class UMCPObservationProfile : public UDataAsset
|
|
30
|
+
{
|
|
31
|
+
GENERATED_BODY()
|
|
32
|
+
public:
|
|
33
|
+
|
|
34
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Values",
|
|
35
|
+
meta=(TitleProperty="Path"))
|
|
36
|
+
TArray<FMCPTrackedValueEntry> TrackedValues;
|
|
37
|
+
|
|
38
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Actors",
|
|
39
|
+
meta=(TitleProperty="ActorId"))
|
|
40
|
+
TArray<FMCPTrackedActorEntry> TrackedActors;
|
|
41
|
+
|
|
42
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Sampling")
|
|
43
|
+
bool bCapturePawnState = true;
|
|
44
|
+
|
|
45
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Sampling")
|
|
46
|
+
bool bCaptureMontage = true;
|
|
47
|
+
|
|
48
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Thresholds")
|
|
49
|
+
float PositionThresholdCm = 5.f;
|
|
50
|
+
|
|
51
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Thresholds")
|
|
52
|
+
float RotationThresholdDeg = 2.f;
|
|
53
|
+
|
|
54
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Thresholds")
|
|
55
|
+
float VelocityThresholdCms = 25.f;
|
|
56
|
+
|
|
57
|
+
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Thresholds")
|
|
58
|
+
float TrackedValueDefaultThreshold = 0.f;
|
|
59
|
+
};
|
|
@@ -19,13 +19,6 @@ namespace UEMCPPIE
|
|
|
19
19
|
{
|
|
20
20
|
namespace
|
|
21
21
|
{
|
|
22
|
-
FString ISOTimestampNow()
|
|
23
|
-
{
|
|
24
|
-
// Approximate ISO 8601 UTC. UE's FDateTime has no timezone awareness,
|
|
25
|
-
// so we treat it as local and append the local offset.
|
|
26
|
-
return FDateTime::Now().ToString(TEXT("%Y-%m-%dT%H:%M:%S"));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
22
|
void SampleTrackedActors(UWorld* World,
|
|
30
23
|
const TArray<FString>& Ids,
|
|
31
24
|
TMap<FString, TWeakObjectPtr<AActor>>& Cache,
|
|
@@ -23,11 +23,6 @@ namespace UEMCPPIE
|
|
|
23
23
|
{
|
|
24
24
|
namespace
|
|
25
25
|
{
|
|
26
|
-
FString ISOTimestampNow()
|
|
27
|
-
{
|
|
28
|
-
return FDateTime::Now().ToString(TEXT("%Y-%m-%dT%H:%M:%S"));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
UInputAction* LoadAction(const FString& Path)
|
|
32
27
|
{
|
|
33
28
|
if (Path.IsEmpty()) return nullptr;
|
|
@@ -338,9 +333,6 @@ namespace UEMCPPIE
|
|
|
338
333
|
SC.AxisThreshold = 0.15f;
|
|
339
334
|
SC.bCapturePawnState = true;
|
|
340
335
|
SC.bCaptureMontage = true;
|
|
341
|
-
// Replay drift compares tracked-value columns recovered from the
|
|
342
|
-
// source CSV header. Telling the sampler about those paths makes
|
|
343
|
-
// Row.TrackedValues populated on each sampled frame.
|
|
344
336
|
SC.TrackedValuePaths = SourceTrackedPaths;
|
|
345
337
|
SC.ClientIndex = Pending.ClientId;
|
|
346
338
|
Sampler.Reset();
|