ue-mcp 1.0.37 → 1.0.38
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
CHANGED
package/package.json
CHANGED
|
@@ -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;
|