More MCP refactors, including creation of MCPAssetFinder.

This commit is contained in:
2026-03-06 17:01:14 -05:00
parent 11dd976441
commit 2c6b5d1ee8
24 changed files with 496 additions and 511 deletions

View File

@@ -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;
}