| 889 | } |
| 890 | |
| 891 | bool ListBlueprintsSync( |
| 892 | TArray<TSharedPtr<FJsonValue>>& OutBlueprints, |
| 893 | FString& OutError) |
| 894 | { |
| 895 | OutBlueprints.Reset(); |
| 896 | OutError.Empty(); |
| 897 | |
| 898 | // Articulation blueprints don't live in one canonical folder -- |
| 899 | // import factories drop them in `/Game/MJCF_Importing` while |
| 900 | // older flows used `/Game/MuJoCoImports`, and users can hand-place |
| 901 | // BPs anywhere. Source-of-truth: any UBlueprint whose parent |
| 902 | // class derives from AMjArticulation is a candidate. |
| 903 | FAssetRegistryModule& AssetRegistry = |
| 904 | FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")); |
| 905 | IAssetRegistry& Registry = AssetRegistry.Get(); |
| 906 | |
| 907 | FARFilter Filter; |
| 908 | Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); |
| 909 | Filter.bRecursivePaths = true; |
| 910 | Filter.PackagePaths.Add(FName(TEXT("/Game"))); |
| 911 | TArray<FAssetData> Assets; |
| 912 | Registry.GetAssets(Filter, Assets); |
| 913 | |
| 914 | const FName ParentClassTag(TEXT("ParentClass")); |
| 915 | for (const FAssetData& Data : Assets) |
| 916 | { |
| 917 | // Read the cached ParentClass tag (asset-registry path) so we |
| 918 | // don't have to LoadObject every BP just to check ancestry. |
| 919 | FString ParentClassExportPath; |
| 920 | if (!Data.GetTagValue(ParentClassTag, ParentClassExportPath)) |
| 921 | continue; |
| 922 | |
| 923 | // ParentClass is stored as e.g. `/Script/URLab.MjArticulation` |
| 924 | // (engine class) or `BlueprintGeneratedClass'/Game/.../parent.parent_C'` |
| 925 | // (BP-derived parent). Resolve via FindObject so we can ask UClass |
| 926 | // about its inheritance chain. |
| 927 | const FString ResolvedPath = |
| 928 | FPackageName::ExportTextPathToObjectPath(ParentClassExportPath); |
| 929 | UClass* ParentClass = FindObject<UClass>(nullptr, *ResolvedPath); |
| 930 | if (!ParentClass) |
| 931 | { |
| 932 | ParentClass = LoadObject<UClass>(nullptr, *ResolvedPath); |
| 933 | } |
| 934 | if (!ParentClass) |
| 935 | continue; |
| 936 | if (!ParentClass->IsChildOf(AMjArticulation::StaticClass())) |
| 937 | continue; |
| 938 | |
| 939 | const FString PackagePath = Data.PackageName.ToString(); |
| 940 | const FString ShortName = Data.AssetName.ToString(); |
| 941 | const FString ClassPath = FString::Printf(TEXT("%s.%s_C"), |
| 942 | *PackagePath, *ShortName); |
| 943 | |
| 944 | TSharedPtr<FJsonObject> Obj = MakeShared<FJsonObject>(); |
| 945 | Obj->SetStringField(TEXT("blueprint_class_path"), ClassPath); |
| 946 | Obj->SetStringField(TEXT("blueprint_short_name"), ShortName); |
| 947 | Obj->SetStringField(TEXT("package_path"), PackagePath); |
| 948 | Obj->SetStringField(TEXT("parent_class"), ParentClass->GetName()); |
no test coverage detected