Big refactor in MCPAssetFinder

This commit is contained in:
2026-03-06 23:51:16 -05:00
parent 7f6438e423
commit 3a2cb775e0
21 changed files with 289 additions and 381 deletions

View File

@@ -41,304 +41,193 @@ void UMCPAssetFinder::Deinitialize()
}
// ============================================================
// GetUpdatedAssets — the gateway for all static API
// Get / Refresh
// ============================================================
UMCPAssetFinder* UMCPAssetFinder::GetUpdatedAssets()
UMCPAssetFinder* UMCPAssetFinder::Get()
{
UMCPAssetFinder* Self = GEngine ? GEngine->GetEngineSubsystem<UMCPAssetFinder>() : nullptr;
checkf(Self, TEXT("MCPAssetFinder::Get() called before engine initialization"));
return Self;
}
void UMCPAssetFinder::CacheAssets(IAssetRegistry &Registry, UClass *Class, bool IncludeSubclasses)
{
FName Key = Class->GetFName();
TArray<FAssetData>& List = AssetCache.Add(Key);
Registry.GetAssetsByClass(Class->GetClassPathName(), List, IncludeSubclasses);
}
void UMCPAssetFinder::Refresh()
{
checkf(IsInGameThread(), TEXT("MCPAssetFinder must only be accessed from the game thread"));
if (!GEngine) return nullptr;
UMCPAssetFinder* Self = GEngine->GetEngineSubsystem<UMCPAssetFinder>();
if (!Self) return nullptr;
UMCPAssetFinder* Self = Get();
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
while (AR.IsLoadingAssets()) FPlatformProcess::Sleep(0.1f);
if (!Self->bDirty) return Self;
if (!Self->bDirty) return;
Self->AllBlueprintAssets.Empty();
Self->AllMapAssets.Empty();
Self->AllBlueprintAndMapAssets.Empty();
Self->AllMaterialAssets.Empty();
Self->AllMaterialInstanceAssets.Empty();
Self->AllMaterialFunctionAssets.Empty();
Self->AllStructAssets.Empty();
Self->AllEnumAssets.Empty();
Self->AssetCache.Empty();
AR.GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), Self->AllBlueprintAssets, true);
AR.GetAssetsByClass(UWorld::StaticClass()->GetClassPathName(), Self->AllMapAssets, false);
AR.GetAssetsByClass(UMaterial::StaticClass()->GetClassPathName(), Self->AllMaterialAssets, false);
AR.GetAssetsByClass(UMaterialInstanceConstant::StaticClass()->GetClassPathName(), Self->AllMaterialInstanceAssets, false);
AR.GetAssetsByClass(UMaterialFunction::StaticClass()->GetClassPathName(), Self->AllMaterialFunctionAssets, false);
AR.GetAssetsByClass(UUserDefinedStruct::StaticClass()->GetClassPathName(), Self->AllStructAssets, false);
AR.GetAssetsByClass(UUserDefinedEnum::StaticClass()->GetClassPathName(), Self->AllEnumAssets, false);
// Cache all asset classes.
Self->CacheAssets(AR, UBlueprint::StaticClass(), true);
Self->CacheAssets(AR, UWorld::StaticClass(), false);
Self->CacheAssets(AR, UMaterial::StaticClass(), false);
Self->CacheAssets(AR, UMaterialInstanceConstant::StaticClass(), false);
Self->CacheAssets(AR, UMaterialFunction::StaticClass(), false);
Self->CacheAssets(AR, UUserDefinedStruct::StaticClass(), false);
Self->CacheAssets(AR, UUserDefinedEnum::StaticClass(), false);
Self->AllBlueprintAndMapAssets = Self->AllBlueprintAssets;
Self->AllBlueprintAndMapAssets.Append(Self->AllMapAssets);
// Combined list: blueprints + maps
TArray<FAssetData>& Combined = Self->AssetCache.Add(BlueprintsAndMaps);
Combined = Self->AssetCache[UBlueprint::StaticClass()->GetFName()];
Combined.Append(Self->AssetCache[UWorld::StaticClass()->GetFName()]);
Self->bDirty = false;
UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Refreshed — BP %d, Map %d, Mat %d, MI %d, MF %d, Struct %d, Enum %d"),
Self->AllBlueprintAssets.Num(), Self->AllMapAssets.Num(), Self->AllMaterialAssets.Num(),
Self->AllMaterialInstanceAssets.Num(), Self->AllMaterialFunctionAssets.Num(),
Self->AllStructAssets.Num(), Self->AllEnumAssets.Num());
return Self;
UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Refreshed — %d asset types cached"),
Self->AssetCache.Num());
}
// ============================================================
// Static asset list accessors
// ============================================================
const TArray<FAssetData>& UMCPAssetFinder::GetBlueprintAssets()
const TArray<FAssetData>& UMCPAssetFinder::GetAssets(FName Key)
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllBlueprintAssets : EmptyAssetArray;
TArray<FAssetData>* Found = Get()->AssetCache.Find(Key);
return Found ? *Found : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetBlueprintAndMapAssets()
const TArray<FAssetData>& UMCPAssetFinder::GetAssets(UClass* Class)
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllBlueprintAndMapAssets : EmptyAssetArray;
return GetAssets(Class->GetFName());
}
const TArray<FAssetData>& UMCPAssetFinder::GetMapAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllMapAssets : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetMaterialAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllMaterialAssets : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetMaterialInstanceAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllMaterialInstanceAssets : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetMaterialFunctionAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllMaterialFunctionAssets : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetStructAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllStructAssets : EmptyAssetArray;
}
const TArray<FAssetData>& UMCPAssetFinder::GetEnumAssets()
{
UMCPAssetFinder* Self = GetUpdatedAssets();
return Self ? Self->AllEnumAssets : EmptyAssetArray;
}
FName UMCPAssetFinder::BlueprintsAndMaps = FName(TEXT("BlueprintsAndMaps"));
// ============================================================
// Find helpers (search cached lists by name or path)
// Find / Search helpers
// ============================================================
namespace
FAssetData* UMCPAssetFinder::FindAsset(FName Class, const FString& NameOrPath, FString* OutError)
{
FAssetData* FindInList(const TArray<FAssetData>& List, const FString& NameOrPath, FString* OutError)
const TArray<FAssetData>& List = GetAssets(Class);
bool IsPath = NameOrPath.Contains(TEXT("/"));
FAssetData* Found = nullptr;
for (const FAssetData& Asset : List)
{
bool IsPath = NameOrPath.Contains(TEXT("/"));
// Short name match — check for duplicates
FAssetData* Found = nullptr;
for (const FAssetData& Asset : List)
FName Name = IsPath ? Asset.PackageName : Asset.AssetName;
if (!Name.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) continue;
if (!Found)
{
FName Name = IsPath ? Asset.PackageName : Asset.AssetName;
if (!Name.ToString().Equals(NameOrPath, ESearchCase::IgnoreCase)) continue;
if (!Found)
{
Found = const_cast<FAssetData*>(&Asset);
continue;
}
if (OutError)
{
*OutError = FString::Printf(
TEXT("Ambiguous asset name '%s' — matches both '%s' and '%s'. Use the full package path to disambiguate."),
*NameOrPath, *Found->PackageName.ToString(), *Asset.PackageName.ToString());
}
return nullptr;
Found = const_cast<FAssetData*>(&Asset);
continue;
}
return Found;
if (OutError)
{
*OutError = FString::Printf(
TEXT("Ambiguous asset name '%s' — matches both '%s' and '%s'. Use the full package path to disambiguate."),
*NameOrPath, *Found->PackageName.ToString(), *Asset.PackageName.ToString());
}
return nullptr;
}
return Found;
}
TArray<FAssetData*> SearchInList(const TArray<FAssetData>& List, const FString& Filter)
FAssetData* UMCPAssetFinder::FindAsset(UClass* Class, const FString& NameOrPath, FString* OutError)
{
return FindAsset(Class->GetFName(), NameOrPath, OutError);
}
TArray<FAssetData*> UMCPAssetFinder::SearchAssets(FName Class, const FString& Filter, FString* OutError)
{
const TArray<FAssetData>& List = GetAssets(Class);
TArray<FAssetData*> Results;
for (const FAssetData& Asset : List)
{
TArray<FAssetData*> Results;
for (const FAssetData& Asset : List)
FString AssetName = Asset.AssetName.ToString();
FString PackagePath = Asset.PackageName.ToString();
if (AssetName.Contains(Filter, ESearchCase::IgnoreCase) ||
PackagePath.Contains(Filter, ESearchCase::IgnoreCase))
{
FString AssetName = Asset.AssetName.ToString();
FString PackagePath = Asset.PackageName.ToString();
if (AssetName.Contains(Filter, ESearchCase::IgnoreCase) ||
PackagePath.Contains(Filter, ESearchCase::IgnoreCase))
{
Results.Add(const_cast<FAssetData*>(&Asset));
}
Results.Add(const_cast<FAssetData*>(&Asset));
}
return Results;
}
return Results;
}
FAssetData* UMCPAssetFinder::FindBlueprintAsset(const FString& NameOrPath, FString* OutError, bool bIncludeLevelBlueprints)
TArray<FAssetData*> UMCPAssetFinder::SearchAssets(UClass* Class, const FString& Filter, FString* OutError)
{
const TArray<FAssetData>& List = bIncludeLevelBlueprints ? GetBlueprintAndMapAssets() : GetBlueprintAssets();
return FindInList(List, NameOrPath, OutError);
}
TArray<FAssetData*> UMCPAssetFinder::SearchBlueprintAssets(const FString& Filter, bool bIncludeLevelBlueprints)
{
const TArray<FAssetData>& List = bIncludeLevelBlueprints ? GetBlueprintAndMapAssets() : GetBlueprintAssets();
return SearchInList(List, Filter);
}
FAssetData* UMCPAssetFinder::FindMapAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetMapAssets(), NameOrPath, OutError);
}
FAssetData* UMCPAssetFinder::FindMaterialAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetMaterialAssets(), NameOrPath, OutError);
}
FAssetData* UMCPAssetFinder::FindMaterialInstanceAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetMaterialInstanceAssets(), NameOrPath, OutError);
}
FAssetData* UMCPAssetFinder::FindMaterialFunctionAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetMaterialFunctionAssets(), NameOrPath, OutError);
}
FAssetData* UMCPAssetFinder::FindStructAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetStructAssets(), NameOrPath, OutError);
}
FAssetData* UMCPAssetFinder::FindEnumAsset(const FString& NameOrPath, FString* OutError)
{
return FindInList(GetEnumAssets(), NameOrPath, OutError);
return SearchAssets(Class->GetFName(), Filter, OutError);
}
FAssetData* UMCPAssetFinder::FindAnyAsset(const FString& NameOrPath, FString* OutError)
{
FAssetData* Asset = FindBlueprintAsset(NameOrPath, OutError);
if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialAsset(NameOrPath, OutError);
if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialInstanceAsset(NameOrPath, OutError);
if (!Asset && (!OutError || OutError->IsEmpty())) Asset = FindMaterialFunctionAsset(NameOrPath, OutError);
return Asset;
FAssetData* Found = nullptr;
for (auto& Pair : Get()->AssetCache)
{
if (Pair.Key == BlueprintsAndMaps) continue;
FString LocalError;
FAssetData* Asset = FindAsset(Pair.Key, NameOrPath, &LocalError);
if (!LocalError.IsEmpty())
{
if (OutError) *OutError = LocalError;
return nullptr;
}
if (!Asset) continue;
if (Found)
{
if (OutError)
{
*OutError = FString::Printf(
TEXT("Ambiguous asset name '%s' — matches '%s' and '%s'. Use the full package path to disambiguate."),
*NameOrPath, *Found->PackageName.ToString(), *Asset->PackageName.ToString());
}
return nullptr;
}
Found = Asset;
}
return Found;
}
// ============================================================
// Load helpers
// ============================================================
UBlueprint* UMCPAssetFinder::LoadBlueprintByName(const FString& NameOrPath, FString& OutError, bool bIncludeLevelBlueprints)
UBlueprint* UMCPAssetFinder::LoadBlueprintOrLevelBlueprint(FAssetData& Asset, FString& OutError)
{
FAssetData* Asset = FindBlueprintAsset(NameOrPath, &OutError, bIncludeLevelBlueprints);
if (!Asset)
{
if (OutError.IsEmpty())
{
OutError = FString::Printf(TEXT("Blueprint '%s' not found. Use list_blueprints to see available assets."), *NameOrPath);
}
return nullptr;
}
// Regular blueprint asset
UBlueprint* BP = Cast<UBlueprint>(Asset->GetAsset());
if (BP)
{
return BP;
}
UBlueprint* BP = Cast<UBlueprint>(Asset.GetAsset());
if (BP) return BP;
// Map asset — extract the level blueprint
UWorld* World = Cast<UWorld>(Asset->GetAsset());
UWorld* World = Cast<UWorld>(Asset.GetAsset());
if (World && World->PersistentLevel)
{
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
if (LevelBP)
{
return LevelBP;
}
if (LevelBP) return LevelBP;
}
OutError = FString::Printf(TEXT("Asset '%s' loaded but its level blueprint could not be retrieved."), *NameOrPath);
OutError = FString::Printf(TEXT("Asset '%s' loaded but its level blueprint could not be retrieved."),
*Asset.AssetName.ToString());
return nullptr;
}
UMaterial* UMCPAssetFinder::LoadMaterialByName(const FString& NameOrPath, FString& OutError)
UBlueprint* UMCPAssetFinder::LoadBlueprintOrLevelBlueprint(const FString& NameOrPath, FString& OutError)
{
FAssetData* Asset = FindMaterialAsset(NameOrPath, &OutError);
if (Asset)
FAssetData* Asset = FindAsset(BlueprintsAndMaps, NameOrPath, &OutError);
if (!Asset)
{
UMaterial* Mat = Cast<UMaterial>(Asset->GetAsset());
if (Mat) return Mat;
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("Blueprint '%s' not found."), *NameOrPath);
return nullptr;
}
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *NameOrPath);
return nullptr;
return LoadBlueprintOrLevelBlueprint(*Asset, OutError);
}
UMaterialInstanceConstant* UMCPAssetFinder::LoadMaterialInstanceByName(const FString& NameOrPath, FString& OutError)
{
FAssetData* Asset = FindMaterialInstanceAsset(NameOrPath, &OutError);
if (Asset)
{
UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(Asset->GetAsset());
if (MI) return MI;
}
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("Material Instance '%s' not found. Use list_materials to see available assets."), *NameOrPath);
return nullptr;
}
UMaterialFunction* UMCPAssetFinder::LoadMaterialFunctionByName(const FString& NameOrPath, FString& OutError)
{
FAssetData* Asset = FindMaterialFunctionAsset(NameOrPath, &OutError);
if (Asset)
{
UMaterialFunction* MF = Cast<UMaterialFunction>(Asset->GetAsset());
if (MF) return MF;
}
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("Material Function '%s' not found. Use list_material_functions to see available assets."), *NameOrPath);
return nullptr;
}
UUserDefinedStruct* UMCPAssetFinder::LoadStructByName(const FString& NameOrPath, FString& OutError)
{
FAssetData* Asset = FindStructAsset(NameOrPath, &OutError);
if (Asset)
{
UUserDefinedStruct* Struct = Cast<UUserDefinedStruct>(Asset->GetAsset());
if (Struct) return Struct;
}
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("UserDefinedStruct '%s' not found."), *NameOrPath);
return nullptr;
}
UUserDefinedEnum* UMCPAssetFinder::LoadEnumByName(const FString& NameOrPath, FString& OutError)
{
FAssetData* Asset = FindEnumAsset(NameOrPath, &OutError);
if (Asset)
{
UUserDefinedEnum* Enum = Cast<UUserDefinedEnum>(Asset->GetAsset());
if (Enum) return Enum;
}
if (OutError.IsEmpty())
OutError = FString::Printf(TEXT("UserDefinedEnum '%s' not found."), *NameOrPath);
return nullptr;
}