More MCP refactors, including creation of MCPAssetFinder.
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
#include "MCPAssetFinder.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/Level.h"
|
||||
#include "Engine/LevelScriptBlueprint.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Materials/MaterialFunction.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
|
||||
const TArray<FAssetData> UMCPAssetFinder::EmptyAssetArray;
|
||||
|
||||
// ============================================================
|
||||
// Initialize / Deinitialize — subscribe to asset registry events
|
||||
// ============================================================
|
||||
|
||||
void UMCPAssetFinder::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
|
||||
AR.OnAssetAdded().AddUObject(this, &UMCPAssetFinder::OnAssetEvent);
|
||||
AR.OnAssetRemoved().AddUObject(this, &UMCPAssetFinder::OnAssetEvent);
|
||||
AR.OnAssetUpdated().AddUObject(this, &UMCPAssetFinder::OnAssetEvent);
|
||||
AR.OnAssetRenamed().AddUObject(this, &UMCPAssetFinder::OnAssetRenamed);
|
||||
}
|
||||
|
||||
void UMCPAssetFinder::Deinitialize()
|
||||
{
|
||||
IAssetRegistry* AR = IAssetRegistry::Get();
|
||||
if (AR)
|
||||
{
|
||||
AR->OnAssetAdded().RemoveAll(this);
|
||||
AR->OnAssetRemoved().RemoveAll(this);
|
||||
AR->OnAssetUpdated().RemoveAll(this);
|
||||
AR->OnAssetRenamed().RemoveAll(this);
|
||||
}
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// GetUpdatedAssets — the gateway for all static API
|
||||
// ============================================================
|
||||
|
||||
UMCPAssetFinder* UMCPAssetFinder::GetUpdatedAssets()
|
||||
{
|
||||
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;
|
||||
|
||||
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
|
||||
|
||||
while (AR.IsLoadingAssets()) FPlatformProcess::Sleep(0.1f);
|
||||
|
||||
if (!Self->bDirty) return Self;
|
||||
|
||||
Self->AllBlueprintAssets.Empty();
|
||||
Self->AllMapAssets.Empty();
|
||||
Self->AllMaterialAssets.Empty();
|
||||
Self->AllMaterialInstanceAssets.Empty();
|
||||
Self->AllMaterialFunctionAssets.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);
|
||||
|
||||
Self->bDirty = false;
|
||||
|
||||
UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Refreshed — BP %d, Map %d, Mat %d, MI %d, MF %d"),
|
||||
Self->AllBlueprintAssets.Num(), Self->AllMapAssets.Num(), Self->AllMaterialAssets.Num(),
|
||||
Self->AllMaterialInstanceAssets.Num(), Self->AllMaterialFunctionAssets.Num());
|
||||
|
||||
return Self;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Static asset list accessors
|
||||
// ============================================================
|
||||
|
||||
const TArray<FAssetData>& UMCPAssetFinder::GetBlueprintAssets()
|
||||
{
|
||||
UMCPAssetFinder* Self = GetUpdatedAssets();
|
||||
return Self ? Self->AllBlueprintAssets : EmptyAssetArray;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Find helpers (search cached lists by name or path)
|
||||
// ============================================================
|
||||
|
||||
namespace
|
||||
{
|
||||
FAssetData* FindInList(const TArray<FAssetData>& List, const FString& NameOrPath, FString* OutError)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return Found;
|
||||
}
|
||||
}
|
||||
|
||||
FAssetData* UMCPAssetFinder::FindBlueprintAsset(const FString& NameOrPath, FString* OutError)
|
||||
{
|
||||
return FindInList(GetBlueprintAssets(), NameOrPath, OutError);
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Load helpers
|
||||
// ============================================================
|
||||
|
||||
UBlueprint* UMCPAssetFinder::LoadBlueprintByName(const FString& NameOrPath, FString& OutError)
|
||||
{
|
||||
// Strategy 1: Try as a regular Blueprint asset
|
||||
FAssetData* Asset = FindBlueprintAsset(NameOrPath, &OutError);
|
||||
if (Asset)
|
||||
{
|
||||
UBlueprint* BP = Cast<UBlueprint>(Asset->GetAsset());
|
||||
if (BP) return BP;
|
||||
}
|
||||
if (!OutError.IsEmpty()) return nullptr;
|
||||
|
||||
// Strategy 2: Try as a level blueprint (from a .umap)
|
||||
FAssetData* MapAsset = FindMapAsset(NameOrPath, &OutError);
|
||||
if (MapAsset)
|
||||
{
|
||||
UWorld* World = Cast<UWorld>(MapAsset->GetAsset());
|
||||
if (World && World->PersistentLevel)
|
||||
{
|
||||
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
|
||||
if (LevelBP)
|
||||
{
|
||||
UE_LOG(LogTemp, Display, TEXT("MCPAssetFinder: Loaded level blueprint from map '%s'"),
|
||||
*NameOrPath);
|
||||
return LevelBP;
|
||||
}
|
||||
}
|
||||
OutError = FString::Printf(TEXT("Map '%s' loaded but its level blueprint could not be retrieved. The map may not contain a level blueprint."), *NameOrPath);
|
||||
return nullptr;
|
||||
}
|
||||
if (!OutError.IsEmpty()) return nullptr;
|
||||
|
||||
OutError = FString::Printf(TEXT("Blueprint or map '%s' not found. Use list_blueprints to see available assets. Level blueprints are referenced by their map name (e.g. 'MAP_Ward')."), *NameOrPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UMaterial* UMCPAssetFinder::LoadMaterialByName(const FString& NameOrPath, FString& OutError)
|
||||
{
|
||||
FAssetData* Asset = FindMaterialAsset(NameOrPath, &OutError);
|
||||
if (Asset)
|
||||
{
|
||||
UMaterial* Mat = Cast<UMaterial>(Asset->GetAsset());
|
||||
if (Mat) return Mat;
|
||||
}
|
||||
if (OutError.IsEmpty())
|
||||
OutError = FString::Printf(TEXT("Material '%s' not found. Use list_materials to see available assets."), *NameOrPath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user